refactor: 统一四个预警历史页面布局结构

- blood-oxygen-alerts.html:更新 stats-card 结构,添加最近预警时间显示
- temperature-alerts.html:完全重构为标准格式,采用与 heart-rate-alerts.html 相同的结构
- stress-alerts.html:已符合标准格式
- 四个页面现采用统一的 stats-card + alert-card 布局,保持各自的颜色主题
- 新增 health-monitor 模块 CHANGELOG.md 文档

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 11:25:11 +08:00
co-authored by Claude Haiku 4.5
parent b8382c862f
commit 852fe14dab
329 changed files with 284757 additions and 21 deletions
@@ -0,0 +1,205 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2; --primary-dark: #0f9b9b; --primary-light: #e6fffb;
--danger: #ff4d4f; --danger-light: #fff1f0;
--text-primary: #1a1a1a; --text-secondary: #666666; --text-muted: #8c8c8c;
--bg-page: #f0f2f5; --bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 100px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 步骤条 */
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; }
/* 信息卡片 */
.info-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.info-card__row {
display: flex; justify-content: space-between; align-items: center;
padding: 12px 0; border-bottom: 1px solid #f0f0f0;
}
.info-card__row:first-child { padding-top: 0; }
.info-card__row:last-child { border-bottom: none; padding-bottom: 0; }
.info-card__label { font-size: 13px; color: var(--text-muted); }
.info-card__value { font-size: 14px; font-weight: 600; color: var(--text-primary); text-align: right; }
.info-card__value--highlight { color: var(--primary-dark); }
/* 须知卡片 */
.tips-card {
background: #fff; border-radius: 20px; padding: 16px; margin-bottom: 14px;
border: 1px solid #eef5f5; box-shadow: 0 8px 24px rgba(0,0,0,0.04);
}
.tips-card__title { font-size: 15px; font-weight: 700; margin-bottom: 10px; }
.tips-card__item {
display: flex; gap: 8px; font-size: 13px; line-height: 1.7;
color: var(--text-secondary); margin-bottom: 8px;
}
.tips-card__item:last-child { margin-bottom: 0; }
.tips-card__dot {
width: 6px; height: 6px; border-radius: 50%; background: var(--primary);
margin-top: 8px; flex-shrink: 0;
}
/* 底部按钮 */
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px;
display: flex; gap: 12px; padding-bottom: 16px;
}
.bottom-actions__btn {
flex: 1; height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.bottom-actions__btn--ghost {
background: var(--danger-light); color: var(--danger); border: 1px solid rgba(255,77,79,0.2);
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19,194,194,0.22);
}
/* Toast */
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.9);
background: rgba(0,0,0,0.75); color: #fff; padding: 14px 22px; border-radius: 12px;
font-size: 13px; line-height: 1.6; text-align: center; z-index: 200;
opacity: 0; pointer-events: none; transition: all 0.25s ease; max-width: 280px;
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- [交互] 步骤条可点击跳转 -->
<div class="stepper">
<!-- [交互] 已提交项目,点击步骤一弹提示,不跳转 -->
<div class="stepper__item stepper__item--done" onclick="showToast()"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></div>
<div class="stepper__line stepper__line--done"></div>
<div class="stepper__item stepper__item--done" onclick="showToast()"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择项目</div></div>
<div class="stepper__line stepper__line--done"></div>
<div class="stepper__item stepper__item--active"><div class="stepper__dot">&#10003;</div><div class="stepper__label">预约日期</div></div>
</div>
<!-- 预约成功信息 -->
<div class="info-card">
<div class="section-title">预约信息</div>
<div class="info-card__row">
<span class="info-card__label">体检医院</span>
<span class="info-card__value">宁夏宝石花医院</span>
</div>
<div class="info-card__row">
<span class="info-card__label">体检员工</span>
<span class="info-card__value">刘韵琴</span>
</div>
<div class="info-card__row">
<span class="info-card__label">体检地址</span>
<span class="info-card__value">银川市西夏区怀远路148号</span>
</div>
<div class="info-card__row">
<span class="info-card__label">体检日期</span>
<span class="info-card__value info-card__value--highlight">2026年3月23日</span>
</div>
<div class="info-card__row">
<span class="info-card__label">体检项目数</span>
<span class="info-card__value info-card__value--highlight">9 项</span>
</div>
</div>
<!-- 体检须知 -->
<div class="tips-card">
<div class="tips-card__title">体检须知</div>
<div class="tips-card__item"><span class="tips-card__dot"></span><span>体检前一天请勿饮酒,避免高脂饮食。</span></div>
<div class="tips-card__item"><span class="tips-card__dot"></span><span>体检当天需空腹,请勿进食或饮水。</span></div>
<div class="tips-card__item"><span class="tips-card__dot"></span><span>请携带本人身份证件按时前往医院。</span></div>
<div class="tips-card__item"><span class="tips-card__dot"></span><span>若需取消预约,请至少提前一天操作,避免影响重新预约。</span></div>
</div>
</div>
</div>
<div class="bottom-actions">
<!-- [交互] 取消预约 -->
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="window.location.href='booking-date.html'">取消预约</button>
<!-- [交互] 查看预约详情,跳转至 my-appointments-detail.html -->
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="window.location.href='my-appointments-detail.html?done=0&total=9'">查看预约详情</button>
</div>
<div class="toast" id="stepToast">当前已预约体检,无法重新选择,请先取消预约</div>
</div>
<script>
let toastTimer = null;
function showToast() {
const toast = document.getElementById('stepToast');
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => { toast.classList.remove('show'); }, 2200);
}
</script>
</body>
</html>
@@ -0,0 +1,417 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2; --primary-dark: #0f9b9b; --primary-light: #e6fffb;
--text-primary: #1a1a1a; --text-secondary: #666666; --text-muted: #8c8c8c;
--bg-page: #f0f2f5; --bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 100px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 步骤条 */
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.fee-banner {
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3; border-radius: 18px; padding: 14px 16px;
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.fee-banner__label { font-size: 13px; color: var(--primary-dark); font-weight: 600; }
.fee-banner__value { font-size: 24px; font-weight: 800; color: var(--primary-dark); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; }
.date-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.calendar-header {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.calendar-header__month { font-size: 17px; font-weight: 700; color: var(--text-primary); }
.calendar-header__nav {
display: flex; gap: 8px;
}
.calendar-header__btn {
width: 32px; height: 32px; border-radius: 10px; border: 1px solid #e8e8e8;
background: #fff; display: flex; align-items: center; justify-content: center;
cursor: pointer; font-size: 14px; color: var(--text-secondary); transition: all 0.2s;
}
.calendar-header__btn:hover { border-color: var(--primary); color: var(--primary); }
.calendar-header__btn:disabled { opacity: 0.3; pointer-events: none; }
.calendar-weekdays {
display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 8px;
}
.calendar-weekday {
text-align: center; font-size: 12px; font-weight: 600; color: var(--text-muted); padding: 6px 0;
}
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.calendar-cell {
display: flex; flex-direction: column; align-items: center; justify-content: center;
height: 52px; border-radius: 12px; cursor: pointer; transition: all 0.2s;
border: 2px solid transparent; background: #f9fbfb; position: relative;
}
.calendar-cell:hover { border-color: var(--primary); }
.calendar-cell.selected { background: var(--primary); border-color: var(--primary); }
.calendar-cell.selected .calendar-cell__day,
.calendar-cell.selected .calendar-cell__slots { color: #fff; }
.calendar-cell.disabled {
opacity: 0.3; pointer-events: none; background: #f5f5f5;
}
.calendar-cell.empty { background: transparent; pointer-events: none; }
.calendar-cell__day { font-size: 15px; font-weight: 700; color: var(--text-primary); line-height: 1; }
.calendar-cell__slots { font-size: 10px; color: var(--primary-dark); font-weight: 600; margin-top: 2px; }
.calendar-cell.disabled .calendar-cell__slots { display: none; }
.calendar-cell__full {
font-size: 10px; color: var(--text-muted); font-weight: 600; margin-top: 2px;
}
.calendar-cell.today .calendar-cell__day {
color: var(--primary-dark);
}
.calendar-cell.today { border-color: rgba(19,194,194,0.3); }
.summary-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.summary-list { display: flex; flex-direction: column; gap: 14px; }
.summary-item { display: flex; justify-content: space-between; gap: 12px; }
.summary-item__label { font-size: 13px; color: var(--text-muted); flex-shrink: 0; }
.summary-item__value { font-size: 14px; font-weight: 600; text-align: right; line-height: 1.6; }
.person-card {
margin-top: 0; background: linear-gradient(135deg, #ffffff 0%, #f8fffe 100%);
border-radius: 20px; padding: 16px; border: 1px solid #e4f7f5; box-shadow: var(--shadow);
}
.person-card__header { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.person-card__avatar {
width: 48px; height: 48px; border-radius: 16px;
background: linear-gradient(135deg, #13c2c2 0%, #5cdbd3 100%);
color: #fff; display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 700; flex-shrink: 0;
}
.person-card__name { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
.person-card__meta { font-size: 12px; color: var(--text-muted); }
.info-list { display: flex; flex-direction: column; gap: 14px; }
.info-item { display: flex; justify-content: space-between; gap: 12px; }
.info-item__label { font-size: 13px; color: var(--text-muted); flex-shrink: 0; }
.info-item__value { font-size: 14px; font-weight: 600; text-align: right; line-height: 1.6; }
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px;
display: flex; gap: 12px; padding-bottom: 16px;
}
.bottom-actions__btn {
flex: 1; height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.bottom-actions__btn--ghost {
background: #f5f5f5; color: var(--text-secondary); border: 1px solid #e8e8e8;
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19,194,194,0.22);
}
/* 确认弹窗 */
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.42); z-index: 120;
display: flex; align-items: center; justify-content: center; padding: 24px;
}
.modal-overlay.hidden { display: none; }
.confirm-modal {
width: 100%; background: #fff; border-radius: 24px; padding: 22px 20px 18px;
box-shadow: 0 20px 50px rgba(0,0,0,0.16);
}
.confirm-modal__title {
font-size: 18px; font-weight: 700; color: var(--text-primary); text-align: center; margin-bottom: 8px;
}
.confirm-modal__desc {
font-size: 13px; color: var(--text-muted); text-align: center; line-height: 1.6; margin-bottom: 18px;
}
.confirm-modal__info {
background: #f7f9fb; border-radius: 16px; padding: 14px 16px; margin-bottom: 18px;
}
.confirm-modal__row {
display: flex; justify-content: space-between; align-items: center; font-size: 14px;
color: var(--text-secondary); margin-bottom: 12px;
}
.confirm-modal__row:last-child { margin-bottom: 0; }
.confirm-modal__value { font-size: 15px; font-weight: 700; color: var(--text-primary); }
.confirm-modal__value--highlight { color: var(--primary-dark); }
.confirm-modal__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.confirm-modal__btn {
height: 44px; border-radius: 22px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.confirm-modal__btn--ghost { background: #f5f5f5; color: var(--text-secondary); }
.confirm-modal__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
}
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.9);
background: rgba(0,0,0,0.75); color: #fff; padding: 14px 22px; border-radius: 12px;
font-size: 13px; line-height: 1.6; text-align: center; z-index: 200;
opacity: 0; pointer-events: none; transition: all 0.25s ease; max-width: 280px;
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- [交互] 步骤条可点击跳转 -->
<div class="stepper">
<!-- [交互] 已提交项目,点击步骤一弹提示,不跳转 -->
<div class="stepper__item stepper__item--done" onclick="showToast()"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></div>
<div class="stepper__line stepper__line--done"></div>
<a class="stepper__item stepper__item--done" href="booking-project-selected.html"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择项目</div></a>
<div class="stepper__line stepper__line--done"></div>
<a class="stepper__item stepper__item--active" href="booking-date.html"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></a>
</div>
<!-- <div class="fee-banner">-->
<!-- <span class="fee-banner__label">您本年度的体检费用(元)</span>-->
<!-- <span class="fee-banner__value">1500.00</span>-->
<!-- </div>-->
<div class="summary-card">
<div class="section-title">体检信息</div>
<div class="summary-list">
<div class="summary-item"><div class="summary-item__label">体检医院</div><div class="summary-item__value">宁夏宝石花医院</div></div>
<div class="summary-item"><div class="summary-item__label">医院地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<!-- <div class="summary-item"><div class="summary-item__label">预计费用</div><div class="summary-item__value">&yen;680</div></div>-->
</div>
</div>
<div class="date-card">
<div class="calendar-header">
<span class="calendar-header__month" id="calendarMonth"></span>
<div class="calendar-header__nav">
<button class="calendar-header__btn" id="prevMonth">&#8249;</button>
<button class="calendar-header__btn" id="nextMonth">&#8250;</button>
</div>
</div>
<div class="calendar-weekdays">
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
</div>
<div class="calendar-grid" id="calendarGrid"></div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="window.location.href='booking-project-selected.html'">上一步</button>
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="openConfirm()">确定预约</button>
</div>
<!-- [交互] 预约二次确认弹窗 -->
<div class="modal-overlay hidden" id="confirmModal">
<div class="confirm-modal">
<div class="confirm-modal__title">确认预约信息</div>
<div class="confirm-modal__desc">请确认以下预约信息无误</div>
<div class="confirm-modal__info">
<div class="confirm-modal__row">
<span>体检医院</span>
<span class="confirm-modal__value">宁夏宝石花医院</span>
</div>
<div class="confirm-modal__row">
<span>体检项目数</span>
<span class="confirm-modal__value" id="confirmCount">9 项</span>
</div>
<div class="confirm-modal__row">
<span>体检日期</span>
<span class="confirm-modal__value confirm-modal__value--highlight" id="confirmDate">--</span>
</div>
</div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--ghost" onclick="closeConfirm()">取消</button>
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="window.location.href='booking-date-selected.html'">确认预约</button>
</div>
</div>
</div>
<div class="toast" id="stepToast">当前已提交项目,如需重新选择医院,请先点击下方重选项目按钮</div>
</div>
<script>
const today = new Date(2026, 2, 16); // 2026-03-16
let currentYear = today.getFullYear();
let currentMonth = today.getMonth();
let selectedDate = null;
const grid = document.getElementById('calendarGrid');
const monthLabel = document.getElementById('calendarMonth');
const prevBtn = document.getElementById('prevMonth');
const nextBtn = document.getElementById('nextMonth');
let toastTimer = null;
function showToast() {
const toast = document.getElementById('stepToast');
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toast.classList.remove('show');
}, 2200);
}
// 模拟可约数据:工作日可约,周末和已过日期不可约
function getSlots(year, month, day) {
const d = new Date(year, month, day);
const dow = d.getDay();
// 已过日期
if (d < new Date(today.getFullYear(), today.getMonth(), today.getDate())) return -1;
// 周末不可约
if (dow === 0 || dow === 6) return 0;
// 模拟剩余人数(用日期做伪随机)
const seed = (day * 7 + month * 13) % 20;
if (seed < 3) return 0; // 约满
return seed + 2;
}
function renderCalendar() {
grid.innerHTML = '';
monthLabel.textContent = `${currentYear}${currentMonth + 1}`;
// 不允许切换到当月之前
prevBtn.disabled = (currentYear === today.getFullYear() && currentMonth <= today.getMonth());
const firstDay = new Date(currentYear, currentMonth, 1);
// 周一为起始(getDay: 0=周日,转为周一起始)
let startDow = firstDay.getDay();
startDow = startDow === 0 ? 6 : startDow - 1;
const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
// 填充前置空格
for (let i = 0; i < startDow; i++) {
const empty = document.createElement('div');
empty.className = 'calendar-cell empty';
grid.appendChild(empty);
}
for (let day = 1; day <= daysInMonth; day++) {
const cell = document.createElement('div');
const slots = getSlots(currentYear, currentMonth, day);
const isToday = (currentYear === today.getFullYear() && currentMonth === today.getMonth() && day === today.getDate());
const isDisabled = slots <= 0;
const isFull = slots === 0;
const isPast = slots === -1;
let cls = 'calendar-cell';
if (isToday) cls += ' today';
if (isDisabled) cls += ' disabled';
if (selectedDate && selectedDate.y === currentYear && selectedDate.m === currentMonth && selectedDate.d === day) {
cls += ' selected';
}
cell.className = cls;
let slotsHtml = '';
if (isPast) {
slotsHtml = '';
} else if (isFull) {
slotsHtml = '<span class="calendar-cell__full">不可约</span>';
} else if (slots > 0) {
slotsHtml = `<span class="calendar-cell__slots">余${slots}</span>`;
}
cell.innerHTML = `<span class="calendar-cell__day">${day}</span>${slotsHtml}`;
if (!isDisabled) {
cell.addEventListener('click', () => {
selectedDate = { y: currentYear, m: currentMonth, d: day };
renderCalendar();
});
}
grid.appendChild(cell);
}
}
prevBtn.addEventListener('click', () => {
currentMonth--;
if (currentMonth < 0) { currentMonth = 11; currentYear--; }
renderCalendar();
});
nextBtn.addEventListener('click', () => {
currentMonth++;
if (currentMonth > 11) { currentMonth = 0; currentYear++; }
renderCalendar();
});
// 默认选中今天
selectedDate = { y: today.getFullYear(), m: today.getMonth(), d: today.getDate() };
renderCalendar();
function openConfirm() {
if (!selectedDate) return;
const dateStr = `${selectedDate.y}${selectedDate.m + 1}${selectedDate.d}`;
document.getElementById('confirmDate').textContent = dateStr;
document.getElementById('confirmModal').classList.remove('hidden');
}
function closeConfirm() {
document.getElementById('confirmModal').classList.add('hidden');
}
</script>
</body>
</html>
@@ -0,0 +1,442 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 120px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 步骤条 */
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.fee-card {
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3;
border-radius: 18px;
padding: 16px 18px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.fee-card__label {
font-size: 13px;
color: var(--primary-dark);
font-weight: 600;
}
.fee-card__amount {
font-size: 26px;
font-weight: 800;
color: var(--primary-dark);
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
margin-top: 6px;
}
.hospital-card {
background: #fff;
border-radius: 18px;
padding: 16px;
margin-bottom: 12px;
border: 2px solid transparent;
box-shadow: var(--shadow);
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.25s ease;
}
.hospital-card.active {
border-color: var(--primary);
background: var(--primary-light);
}
.hospital-card__info {
display: flex;
align-items: center;
gap: 12px;
}
.hospital-card__icon {
width: 42px;
height: 42px;
border-radius: 14px;
background: linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.hospital-card__name {
font-size: 15px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 2px;
}
.hospital-card__addr {
font-size: 12px;
color: var(--text-muted);
}
.radio-dot {
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid #d9d9d9;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s ease;
flex-shrink: 0;
}
.hospital-card.active .radio-dot {
border-color: var(--primary);
background: var(--primary);
}
.radio-dot::after {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background: #fff;
opacity: 0;
transition: opacity 0.25s ease;
}
.hospital-card.active .radio-dot::after { opacity: 1; }
.person-card {
margin-top: 20px;
background: linear-gradient(135deg, #ffffff 0%, #f8fffe 100%);
border-radius: 20px;
padding: 16px;
border: 1px solid #e4f7f5;
box-shadow: var(--shadow);
}
.person-card__header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.person-card__avatar {
width: 48px;
height: 48px;
border-radius: 16px;
background: linear-gradient(135deg, #13c2c2 0%, #5cdbd3 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: 700;
flex-shrink: 0;
}
.person-card__name {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 4px;
}
.person-card__meta {
font-size: 12px;
color: var(--text-muted);
}
.info-list { display: flex; flex-direction: column; gap: 14px; }
.info-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.info-item__label { font-size: 13px; color: var(--text-muted); }
.info-item__value { font-size: 14px; color: var(--text-primary); font-weight: 600; }
.bottom-actions {
position: absolute;
left: 16px;
right: 16px;
bottom: 18px;
padding-bottom: 16px;
}
.bottom-actions__btn {
width: 100%;
height: 48px;
border-radius: 24px;
font-size: 15px;
font-weight: 600;
border: none;
cursor: pointer;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
.nav-right {
position: absolute; right: 16px; font-size: 13px; color: var(--primary);
font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 4px;
}
/* 弹窗 */
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.45); z-index: 100;
display: flex; align-items: center; justify-content: center;
transition: opacity 0.25s ease;
}
.modal-overlay.hidden { display: none; }
.modal-card {
width: 340px; background: #fff; border-radius: 24px; padding: 24px 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.18); position: relative;
}
.modal-card__title {
font-size: 18px; font-weight: 700; color: var(--text-primary);
text-align: center; margin-bottom: 6px;
}
.modal-card__sub {
font-size: 13px; color: var(--text-muted); text-align: center; margin-bottom: 20px;
}
.modal-field {
display: flex; justify-content: space-between; align-items: center;
padding: 14px 0; border-bottom: 1px solid #f0f0f0;
}
.modal-field:last-of-type { border-bottom: none; }
.modal-field__label { font-size: 14px; color: var(--text-muted); }
.modal-field__value { font-size: 14px; color: var(--text-primary); font-weight: 600; }
.marriage-toggle {
display: inline-flex; background: #f5f5f5; border-radius: 20px; overflow: hidden;
}
.marriage-toggle__btn {
padding: 6px 16px; font-size: 13px; font-weight: 600; border: none;
background: transparent; color: var(--text-muted); cursor: pointer;
border-radius: 20px; transition: all 0.2s ease;
}
.marriage-toggle__btn.active {
background: var(--primary); color: #fff;
}
.modal-confirm-btn {
margin-top: 20px; width: 100%; height: 44px; border-radius: 22px;
font-size: 15px; font-weight: 600; border: none; cursor: pointer;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 8px 20px rgba(19, 194, 194, 0.2);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
<!-- [交互] 点击打开个人信息弹窗 -->
<span class="nav-right" onclick="openModal()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
个人信息
</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- 步骤条 -->
<!-- [交互] 步骤条可点击跳转 -->
<div class="stepper">
<a class="stepper__item stepper__item--active" href="booking-hospital.html"><div class="stepper__dot">1</div><div class="stepper__label">选择医院</div></a>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">2</div><div class="stepper__label">选择项目</div></div>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<!-- 费用展示 -->
<div class="fee-card">
<div class="fee-card__label">您本年度的体检费用(元)</div>
<div class="fee-card__amount">1500.00</div>
</div>
<!-- 医院列表 -->
<div class="section-title">选择医院</div>
<div class="hospital-card active" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">宁夏宝石花医院</div><div class="hospital-card__addr">宁夏银川市</div></div>
</div>
<div class="radio-dot"></div>
</div>
<div class="hospital-card" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">长庆星龙园医院</div><div class="hospital-card__addr">陕西西安市</div></div>
</div>
<div class="radio-dot"></div>
</div>
<div class="hospital-card" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">西安长庆医院</div><div class="hospital-card__addr">陕西西安市</div></div>
</div>
<div class="radio-dot"></div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn" onclick="window.location.href='booking-project.html'">下一步</button>
</div>
<!-- [交互] 个人信息确认弹窗,仅从 booking-type 进入时自动弹出 -->
<div class="modal-overlay hidden" id="personModal">
<div class="modal-card">
<div class="modal-card__title">确认个人信息</div>
<div class="modal-card__sub">请核对以下信息,确认无误后继续预约</div>
<div class="modal-field">
<span class="modal-field__label">姓名</span>
<span class="modal-field__value">刘韵琴</span>
</div>
<div class="modal-field">
<span class="modal-field__label">单位</span>
<span class="modal-field__value">长庆油田第一采油厂</span>
</div>
<div class="modal-field">
<span class="modal-field__label">身份证号</span>
<span class="modal-field__value">6101021994********</span>
</div>
<div class="modal-field">
<span class="modal-field__label">性别</span>
<span class="modal-field__value"></span>
</div>
<div class="modal-field">
<span class="modal-field__label">婚姻状况</span>
<div class="marriage-toggle">
<button class="marriage-toggle__btn active" onclick="toggleMarriage(this)">已婚</button>
<button class="marriage-toggle__btn" onclick="toggleMarriage(this)">未婚</button>
</div>
</div>
<button class="modal-confirm-btn" onclick="closeModal()">确认</button>
</div>
</div>
</div>
<script>
// [交互] 仅从 booking-type 跳转进入时自动弹出个人信息确认弹窗
if (new URLSearchParams(window.location.search).get('from') === 'booking-type') {
document.getElementById('personModal').classList.remove('hidden');
}
function selectHospital(el) {
document.querySelectorAll('.hospital-card').forEach(c => c.classList.remove('active'));
el.classList.add('active');
}
function openModal() {
document.getElementById('personModal').classList.remove('hidden');
}
function closeModal() {
document.getElementById('personModal').classList.add('hidden');
}
function toggleMarriage(btn) {
document.querySelectorAll('.marriage-toggle__btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
}
</script>
</body>
</html>
@@ -0,0 +1,290 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2; --primary-dark: #0f9b9b; --primary-light: #e6fffb;
--text-primary: #1a1a1a; --text-secondary: #666666; --text-muted: #8c8c8c;
--bg-page: #f0f2f5; --bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 100px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 步骤条 */
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.fee-banner {
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3; border-radius: 18px; padding: 14px 16px;
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.fee-banner__label { font-size: 13px; color: var(--primary-dark); font-weight: 600; }
.fee-banner__value { font-size: 24px; font-weight: 800; color: var(--primary-dark); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; }
/* 基本信息卡片 */
.info-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.info-card__row {
display: flex; justify-content: space-between; align-items: center;
padding: 12px 0; border-bottom: 1px solid #f0f0f0;
}
.info-card__row:first-child { padding-top: 0; }
.info-card__row:last-child { border-bottom: none; padding-bottom: 0; }
.info-card__label { font-size: 13px; color: var(--text-muted); }
.info-card__value { font-size: 14px; font-weight: 600; color: var(--text-primary); text-align: right; }
.info-card__value--highlight { color: var(--primary-dark); font-size: 18px; font-weight: 800; }
.info-card__sub {
display: flex; gap: 16px; justify-content: flex-end; margin-top: 4px;
}
.info-card__sub-item { font-size: 12px; color: var(--text-muted); }
.info-card__sub-value { font-weight: 600; color: var(--text-secondary); }
/* 项目列表区 */
.project-section {
background: #fff; border-radius: 20px; padding: 18px 16px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.project-section__header {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.project-section__count { font-size: 13px; color: var(--text-muted); }
.project-section__count em { font-style: normal; color: var(--primary-dark); font-weight: 700; }
.project-tabs {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
padding: 5px; background: #f5f7f7; border-radius: 14px; margin-bottom: 14px;
}
.project-tab {
height: 34px; border: none; border-radius: 10px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
}
.project-tab--active {
background: #fff; color: var(--primary-dark);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.project-list-panel {
max-height: 280px; overflow-y: auto; padding-right: 2px;
}
.project-list-panel::-webkit-scrollbar { display: none; }
.project-list { display: flex; flex-direction: column; gap: 10px; }
.project-item {
display: flex; align-items: center; gap: 12px; padding: 12px 14px;
background: #f9fbfb; border-radius: 14px; border: 1px solid #eef5f5;
}
.project-item__dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--primary); flex-shrink: 0;
}
.project-item__info { flex: 1; }
.project-item__name { font-size: 14px; font-weight: 600; margin-bottom: 2px; }
.project-item__desc { font-size: 12px; color: var(--text-muted); }
.project-item__price { font-size: 13px; font-weight: 700; color: var(--primary-dark); flex-shrink: 0; }
.project-item__badge {
display: inline-flex; align-items: center; height: 20px; padding: 0 8px;
border-radius: 10px; font-size: 10px; font-weight: 600; flex-shrink: 0;
}
.project-item__badge--normal { background: #f5f5f5; color: var(--text-muted); }
.project-item__badge--key { background: #fff1f0; color: #ff4d4f; }
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px;
display: flex; gap: 12px; padding-bottom: 16px;
}
.bottom-actions__btn {
flex: 1; height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.bottom-actions__btn--ghost {
background: #f5f5f5; color: var(--text-secondary); border: 1px solid #e8e8e8;
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19,194,194,0.22);
}
/* Toast 提示 */
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.9);
background: rgba(0,0,0,0.75); color: #fff; padding: 14px 22px; border-radius: 12px;
font-size: 13px; line-height: 1.6; text-align: center; z-index: 200;
opacity: 0; pointer-events: none; transition: all 0.25s ease; max-width: 280px;
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- [交互] 步骤条可点击跳转 -->
<div class="stepper">
<!-- [交互] 已提交项目,点击步骤一弹提示,不跳转 -->
<div class="stepper__item stepper__item--done" onclick="showToast()"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></div>
<div class="stepper__line stepper__line--done"></div>
<a class="stepper__item stepper__item--active" href="booking-project.html"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择项目</div></a>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<!-- 基本信息卡片 -->
<div class="info-card">
<div class="info-card__row">
<span class="info-card__label">承检医院</span>
<span class="info-card__value">宁夏宝石花医院</span>
</div>
<div class="info-card__row">
<span class="info-card__label">体检额度</span>
<span class="info-card__value info-card__value--highlight" >¥1500.00</span>
</div>
<div class="info-card__row">
<span class="info-card__label">已选费用合计</span>
<span class="info-card__value info-card__value--highlight" id="totalFeeDisplay">¥860.00</span>
</div>
<div class="info-card__row" style="flex-wrap:wrap">
<span class="info-card__label">费用包含</span>
<div class="info-card__sub">
<span class="info-card__sub-item">必检 <span class="info-card__sub-value" id="requiredFeeDisplay">¥720.00</span></span>
<span class="info-card__sub-item">选检 <span class="info-card__sub-value" id="optionalFeeDisplay">¥140.00</span></span>
</div>
</div>
</div>
<!-- 体检项目列表 -->
<div class="project-section">
<div class="project-section__header">
<div class="section-title" style="margin-bottom:0">已选体检项目</div>
<span class="project-section__count"><em id="totalCount">9</em></span>
</div>
<!-- [交互] 点击切换自选项目/必检项目 -->
<div class="project-tabs">
<button class="project-tab project-tab--active" type="button" data-tab="optional">自选项目(1)</button>
<button class="project-tab" type="button" data-tab="required">必检项目(8)</button>
</div>
<div class="project-list-panel">
<div class="project-list" id="projectList"></div>
</div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="window.location.href='booking-project.html'">重选项目</button>
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="window.location.href='booking-date.html'">下一步</button>
</div>
<div class="toast" id="stepToast">当前已提交项目,如需重新选择医院,请先点击下方重选项目按钮</div>
</div>
<script>
const projects = [
{ name: '一般检查', desc: '身高、体重、血压、视力', price: 120, type: 'required', level: 'normal' },
{ name: '血常规', desc: '血红蛋白、白细胞、血小板等', price: 80, type: 'required', level: 'normal' },
{ name: '尿常规', desc: '尿蛋白、尿糖、尿潜血等', price: 45, type: 'required', level: 'normal' },
{ name: '肝功能', desc: 'ALT、AST、总胆红素等', price: 160, type: 'required', level: 'key' },
{ name: '肾功能', desc: '肌酐、尿素氮、尿酸等', price: 110, type: 'required', level: 'key' },
{ name: '血脂四项', desc: '总胆固醇、甘油三酯、HDL、LDL', price: 90, type: 'required', level: 'normal' },
{ name: '空腹血糖', desc: '空腹静脉血糖检测', price: 40, type: 'required', level: 'normal' },
{ name: '心电图', desc: '十二导联心电图检查', price: 75, type: 'required', level: 'key' },
{ name: '胸部X光', desc: '胸部正位X线摄影', price: 140, type: 'optional', level: 'normal' }
];
let activeTab = 'optional';
const listEl = document.getElementById('projectList');
const tabButtons = document.querySelectorAll('.project-tab');
let toastTimer = null;
function showToast() {
const toast = document.getElementById('stepToast');
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toast.classList.remove('show');
}, 2200);
}
function renderList() {
const items = projects.filter(p => p.type === activeTab);
listEl.innerHTML = items.map(p => `
<div class="project-item">
<div class="project-item__dot"></div>
<div class="project-item__info">
<div class="project-item__name">${p.name}</div>
<div class="project-item__desc">${p.desc}</div>
</div>
<span class="project-item__badge project-item__badge--${p.level === 'key' ? 'key' : 'normal'}">${p.level === 'key' ? '重点' : '一般'}</span>
<span class="project-item__price">¥${p.price.toFixed(2)}</span>
</div>
`).join('');
}
tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
activeTab = btn.dataset.tab;
tabButtons.forEach(b => b.classList.remove('project-tab--active'));
btn.classList.add('project-tab--active');
renderList();
});
});
renderList();
</script>
</body>
</html>
@@ -0,0 +1,453 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-light: #f6ffed;
--success-text: #52c41a;
--danger: #ff4d4f;
--danger-light: #fff1f0;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 112px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 步骤条 */
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
/* 费用卡片 */
.fee-banner {
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3; border-radius: 18px; padding: 14px 16px;
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.fee-banner__label { font-size: 13px; color: var(--primary-dark); font-weight: 600; }
.fee-banner__value { font-size: 24px; font-weight: 800; color: var(--primary-dark); }
/* 项目列表 */
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; }
.project-tabs {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
padding: 6px; background: rgba(255,255,255,0.92); border-radius: 18px;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.08); margin-bottom: 14px;
}
.project-tab {
height: 36px; border: none; border-radius: 14px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
}
.project-tab--active {
background: var(--primary-light); color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.project-list-panel {
max-height: 432px; overflow-y: auto; padding-right: 2px; margin-bottom: 12px;
}
.project-list-panel::-webkit-scrollbar { display: none; }
.project-grid { display: flex; flex-direction: column; gap: 10px; }
.project-card {
background: #fff; border-radius: 14px; padding: 14px 16px; border: 2px solid #eef5f5;
cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
}
.project-card:hover { border-color: var(--primary); }
.project-card.selected { border-color: var(--primary); background: var(--primary-light); }
.project-card.project-card--readonly {
cursor: default; background: #fff; border-color: #eef5f5;
}
.project-card.project-card--readonly:hover { border-color: #eef5f5; }
.project-card__check {
width: 22px; height: 22px; border-radius: 6px; border: 2px solid #d9d9d9;
display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: all 0.2s;
}
.project-card__check--hidden { visibility: hidden; }
.project-card__check::after {
content: ''; width: 8px; height: 8px; border-radius: 2px; background: #fff; opacity: 0;
}
.project-card.selected .project-card__check { background: var(--primary); border-color: var(--primary); }
.project-card.selected .project-card__check::after { opacity: 1; }
.project-card__info { flex: 1; min-width: 0; }
.project-card__name { font-size: 14px; font-weight: 600; margin-bottom: 2px; }
.project-card__desc { font-size: 12px; color: var(--text-muted); }
.project-card__side {
flex-shrink: 0; display: flex; align-items: center; gap: 10px;
margin-left: auto; padding-left: 8px;
}
.project-card__tags {
display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
.project-card__price { font-size: 13px; color: var(--primary-dark); font-weight: 700; }
.project-card__badge {
display: inline-flex; align-items: center; justify-content: center; height: 22px;
padding: 0 10px; border-radius: 11px; background: #f5f5f5;
color: var(--text-muted); font-size: 11px; font-weight: 600;
}
.project-card__badge--key {
background: #fff1f0; color: #ff4d4f;
}
.project-card__arrow {
flex-shrink: 0; color: #d0d0d0; font-size: 16px; cursor: pointer;
transition: color 0.2s;
}
.project-card__arrow:hover { color: var(--primary); }
/* 底部按钮 */
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px;
display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding-bottom: 16px;
}
.summary-card {
background: #fff; border-radius: 18px; padding: 12px 14px;
box-shadow: 0 10px 24px rgba(19, 194, 194, 0.10); border: 1px solid rgba(19, 194, 194, 0.08);
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px;
margin-bottom: 14px;
}
.summary-card__title {
display: none;
}
.summary-row {
display: flex; justify-content: space-between; align-items: center;
font-size: 11px; color: var(--text-secondary); margin-bottom: 0; gap: 8px;
}
.summary-row:last-child { margin-bottom: 0; }
.summary-row__value { font-weight: 700; color: var(--text-primary); white-space: nowrap; }
.summary-row__value--highlight { color: var(--primary-dark); font-size: 13px; }
.summary-row__value--danger { color: var(--danger); }
.bottom-actions__btn {
width: 100%; height: 56px; border-radius: 18px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.bottom-actions__btn--ghost {
background: #fff; color: var(--text-secondary); border: 1px solid #d9e8e8;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.04);
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
/* 提交确认弹窗 */
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.42); z-index: 120;
display: flex; align-items: center; justify-content: center; padding: 24px;
}
.modal-overlay.hidden { display: none; }
.confirm-modal {
width: 100%; background: #fff; border-radius: 24px; padding: 22px 20px 18px;
box-shadow: 0 20px 50px rgba(0,0,0,0.16);
}
.confirm-modal__title {
font-size: 18px; font-weight: 700; color: var(--text-primary); text-align: center; margin-bottom: 8px;
}
.confirm-modal__desc {
font-size: 13px; color: var(--text-muted); text-align: center; line-height: 1.6; margin-bottom: 18px;
}
.confirm-modal__info {
background: #f7f9fb; border-radius: 16px; padding: 14px 16px; margin-bottom: 18px;
}
.confirm-modal__row {
display: flex; justify-content: space-between; align-items: center; font-size: 14px;
color: var(--text-secondary); margin-bottom: 12px;
}
.confirm-modal__row:last-child { margin-bottom: 0; }
.confirm-modal__value { font-size: 16px; font-weight: 700; color: var(--text-primary); }
.confirm-modal__value--highlight { color: var(--primary-dark); }
.confirm-modal__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.confirm-modal__btn {
height: 44px; border-radius: 22px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.confirm-modal__btn--ghost { background: #f5f5f5; color: var(--text-secondary); }
.confirm-modal__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- [交互] 步骤条可点击跳转 -->
<div class="stepper">
<a class="stepper__item stepper__item--done" href="booking-hospital.html?from=stepper"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></a>
<div class="stepper__line stepper__line--done"></div>
<a class="stepper__item stepper__item--active" href="booking-project.html"><div class="stepper__dot">2</div><div class="stepper__label">选择项目</div></a>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<!-- 费用 -->
<div class="fee-banner">
<span class="fee-banner__label">您本年度的体检费用(元)</span>
<span class="fee-banner__value">1500.00</span>
</div>
<div class="summary-card">
<div class="summary-card__title">费用汇总</div>
<div class="summary-row">
<span>已选</span>
<span class="summary-row__value summary-row__value--highlight" id="totalFee">¥0.00</span>
</div>
<div class="summary-row">
<span>必检项目</span>
<span class="summary-row__value" id="requiredFee">¥0.00</span>
</div>
<div class="summary-row">
<span>选检项目</span>
<span class="summary-row__value" id="optionalFee">¥0.00</span>
</div>
<div class="summary-row">
<span>可用额度</span>
<span class="summary-row__value" id="availableFee">¥0.00</span>
</div>
</div>
<!-- 项目列表 -->
<div class="section-title">选择体检项目</div>
<!-- [交互] 点击切换必检项目/选检项目 -->
<div class="project-tabs" id="projectTabs">
<button class="project-tab project-tab--active" type="button" data-tab="optional">选检项目</button>
<button class="project-tab" type="button" data-tab="required">必检项目</button>
</div>
<div class="project-list-panel">
<div class="project-grid" id="projectGrid"></div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn bottom-actions__btn--ghost" type="button" onclick="goPrevStep()">上一步</button>
<button class="bottom-actions__btn bottom-actions__btn--primary" type="button" onclick="openSubmitConfirm()">提交项目</button>
</div>
<!-- [交互] 提交二次确认弹窗 -->
<div class="modal-overlay hidden" id="submitModal">
<div class="confirm-modal">
<div class="confirm-modal__title">确认提交</div>
<div class="confirm-modal__desc">请确认以下体检项目信息无误</div>
<div class="confirm-modal__info">
<div class="confirm-modal__row">
<span>体检项目数</span>
<span class="confirm-modal__value" id="confirmCount">0 项</span>
</div>
<div class="confirm-modal__row">
<span>总体检费用</span>
<span class="confirm-modal__value confirm-modal__value--highlight" id="confirmFee">¥0.00</span>
</div>
</div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--ghost" onclick="closeSubmitConfirm()">取消</button>
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="confirmSubmit()">确认提交</button>
</div>
</div>
</div>
</div>
<script>
const annualLimit = 1500;
const projects = [
{ id: 'general', name: '一般检查', desc: '身高、体重、血压、视力', price: 120, type: 'required', level: 'normal' },
{ id: 'blood', name: '血常规', desc: '血红蛋白、白细胞、血小板等', price: 80, type: 'required', level: 'normal' },
{ id: 'urine', name: '尿常规', desc: '尿蛋白、尿糖、尿潜血等', price: 45, type: 'required', level: 'normal' },
{ id: 'liver', name: '肝功能', desc: 'ALT、AST、总胆红素等', price: 160, type: 'required', level: 'key' },
{ id: 'kidney', name: '肾功能', desc: '肌酐、尿素氮、尿酸等', price: 110, type: 'required', level: 'key' },
{ id: 'lipid', name: '血脂四项', desc: '总胆固醇、甘油三酯、HDL、LDL', price: 90, type: 'required', level: 'normal' },
{ id: 'glucose', name: '空腹血糖', desc: '空腹静脉血糖检测', price: 40, type: 'required', level: 'normal' },
{ id: 'ecg', name: '心电图', desc: '十二导联心电图检查', price: 75, type: 'required', level: 'key' },
{ id: 'xray', name: '胸部X光', desc: '胸部正位X线摄影', price: 140, type: 'optional', level: 'normal' },
{ id: 'ultrasound', name: '腹部B超', desc: '肝胆脾胰肾超声检查', price: 180, type: 'optional', level: 'key' },
{ id: 'thyroid', name: '甲状腺彩超', desc: '甲状腺结构与结节筛查', price: 160, type: 'optional', level: 'key' },
{ id: 'tumor', name: '肿瘤标志物', desc: '基础肿瘤风险筛查', price: 220, type: 'optional', level: 'key' }
];
let activeTab = 'optional';
const selectedOptionalIds = new Set(['xray']);
const grid = document.getElementById('projectGrid');
const tabButtons = document.querySelectorAll('.project-tab');
const totalFeeEl = document.getElementById('totalFee');
const requiredFeeEl = document.getElementById('requiredFee');
const optionalFeeEl = document.getElementById('optionalFee');
const availableFeeEl = document.getElementById('availableFee');
function formatPrice(value) {
return `¥${value.toFixed(2)}`;
}
function getRequiredProjects() {
return projects.filter(project => project.type === 'required');
}
function getOptionalProjects() {
return projects.filter(project => project.type === 'optional');
}
function getRequiredFee() {
return getRequiredProjects().reduce((sum, project) => sum + project.price, 0);
}
function getOptionalFee() {
return getOptionalProjects()
.filter(project => selectedOptionalIds.has(project.id))
.reduce((sum, project) => sum + project.price, 0);
}
function updateSummary() {
const requiredFee = getRequiredFee();
const optionalFee = getOptionalFee();
const totalFee = requiredFee + optionalFee;
const availableFee = annualLimit - totalFee;
requiredFeeEl.textContent = formatPrice(requiredFee);
optionalFeeEl.textContent = formatPrice(optionalFee);
totalFeeEl.textContent = formatPrice(totalFee);
availableFeeEl.textContent = formatPrice(Math.max(availableFee, 0));
availableFeeEl.classList.toggle('summary-row__value--danger', availableFee < 0);
}
function renderProjects() {
const currentProjects = activeTab === 'required' ? getRequiredProjects() : getOptionalProjects();
grid.innerHTML = '';
currentProjects.forEach(project => {
const card = document.createElement('div');
const isReadonly = project.type === 'required';
const isSelected = selectedOptionalIds.has(project.id);
const projectLevelText = project.level === 'key' ? '重点项目' : '一般项目';
card.className = `project-card${isReadonly ? ' project-card--readonly' : ''}${isSelected ? ' selected' : ''}`;
card.innerHTML = `
<div class="project-card__check${isReadonly ? ' project-card__check--hidden' : ''}"></div>
<div class="project-card__info">
<div class="project-card__name">${project.name}</div>
<div class="project-card__desc">${project.desc}</div>
</div>
<div class="project-card__side">
<div class="project-card__tags">
<span class="project-card__price">${formatPrice(project.price)}</span>
<span class="project-card__badge${project.level === 'key' ? ' project-card__badge--key' : ''}">${projectLevelText}</span>
</div>
<span class="project-card__arrow" data-detail="${project.id}">&#8250;</span>
</div>
`;
// [交互] 箭头点击跳转项目详情
card.querySelector('.project-card__arrow').addEventListener('click', (e) => {
e.stopPropagation();
window.location.href = `project-detail.html?id=${project.id}`;
});
if (!isReadonly) {
card.addEventListener('click', () => {
if (selectedOptionalIds.has(project.id)) {
selectedOptionalIds.delete(project.id);
} else {
selectedOptionalIds.add(project.id);
}
renderProjects();
updateSummary();
});
}
grid.appendChild(card);
});
}
tabButtons.forEach(button => {
button.addEventListener('click', () => {
activeTab = button.dataset.tab;
tabButtons.forEach(item => item.classList.remove('project-tab--active'));
button.classList.add('project-tab--active');
renderProjects();
});
});
renderProjects();
updateSummary();
function openSubmitConfirm() {
const requiredCount = getRequiredProjects().length;
const optionalCount = selectedOptionalIds.size;
const totalCount = requiredCount + optionalCount;
const totalFee = getRequiredFee() + getOptionalFee();
document.getElementById('confirmCount').textContent = totalCount + ' 项';
document.getElementById('confirmFee').textContent = formatPrice(totalFee);
document.getElementById('submitModal').classList.remove('hidden');
}
function closeSubmitConfirm() {
document.getElementById('submitModal').classList.add('hidden');
}
function goPrevStep() {
window.location.href = 'booking-hospital.html';
}
function confirmSubmit() {
window.location.href = 'booking-project-selected.html';
}
</script>
</body>
</html>
@@ -0,0 +1,262 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 120px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.section-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 6px;
}
.section-desc {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 20px;
}
.type-card {
background: #fff;
border-radius: 20px;
padding: 20px 18px;
margin-bottom: 14px;
border: 2px solid transparent;
box-shadow: var(--shadow);
cursor: pointer;
transition: all 0.25s ease;
}
.type-card.active {
border-color: var(--primary);
background: var(--primary-light);
}
.type-card__header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 10px;
}
.type-card__icon {
width: 48px;
height: 48px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
}
.type-card__icon--routine {
background: linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%);
}
.type-card__icon--gastro {
background: linear-gradient(135deg, #fff7e6 0%, #ffe7ba 100%);
}
.type-card__name {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
}
.type-card__desc {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.7;
}
.type-card__radio {
position: absolute;
right: 18px;
top: 50%;
transform: translateY(-50%);
}
.type-card__header { position: relative; }
.radio-dot {
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid #d9d9d9;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s ease;
flex-shrink: 0;
margin-left: auto;
}
.type-card.active .radio-dot {
border-color: var(--primary);
background: var(--primary);
}
.radio-dot::after {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background: #fff;
opacity: 0;
transition: opacity 0.25s ease;
}
.type-card.active .radio-dot::after { opacity: 1; }
.bottom-actions {
position: absolute;
left: 16px;
right: 16px;
bottom: 18px;
padding-bottom: 16px;
}
.bottom-actions__btn {
width: 100%;
height: 48px;
border-radius: 24px;
font-size: 15px;
font-weight: 600;
border: none;
cursor: pointer;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回上一页 -->
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">体检预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="section-title">选择体检类型</div>
<div class="section-desc">请根据需求选择适合的体检项目</div>
<!-- [交互] 点击切换选中态,单选 -->
<div class="type-card active" onclick="selectType(this, 'routine')">
<div class="type-card__header">
<div class="type-card__icon type-card__icon--routine">🩺</div>
<div class="type-card__name">常规体检</div>
<div class="radio-dot"></div>
</div>
<div class="type-card__desc">基础健康综合筛查,覆盖核心指标</div>
</div>
<div class="type-card" onclick="selectType(this, 'gastro')">
<div class="type-card__header">
<div class="type-card__icon type-card__icon--gastro">🔬</div>
<div class="type-card__name">胃肠镜体检</div>
<div class="radio-dot"></div>
</div>
<div class="type-card__desc">胃肠内镜专项检查,直观诊断消化道病变</div>
</div>
</div>
</div>
<div class="bottom-actions">
<!-- [交互] 根据选中的体检类型跳转到对应流程 -->
<button class="bottom-actions__btn" onclick="goNext()">确定</button>
</div>
</div>
<script>
var selectedType = 'routine';
function selectType(el, type) {
document.querySelectorAll('.type-card').forEach(c => c.classList.remove('active'));
el.classList.add('active');
selectedType = type;
}
function goNext() {
if (selectedType === 'gastro') {
window.location.href = 'gastro-hospital.html?from=booking-type';
} else {
window.location.href = 'booking-hospital.html?from=booking-type';
}
}
</script>
</body>
</html>
+317
View File
@@ -0,0 +1,317 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>一键咨询</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--danger: #e84c4c;
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: #f5f5f5;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 0 0 28px; }
/* 大分类 */
.category {
background: #fff;
margin-bottom: 10px;
padding: 18px 16px 6px;
}
.category__title {
font-size: 17px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
}
/* 子分类卡片 */
.sub-section {
margin-bottom: 18px;
}
.sub-section__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.sub-section__name {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
font-weight: 700;
color: var(--text-primary);
}
.sub-section__bar {
width: 4px;
height: 18px;
border-radius: 2px;
background: var(--primary);
flex-shrink: 0;
}
.sub-section__link {
font-size: 13px;
color: var(--danger);
font-weight: 600;
text-decoration: none;
cursor: pointer;
white-space: nowrap;
}
/* 表格 */
.consult-table {
width: 100%;
border-collapse: collapse;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e8e8e8;
}
.consult-table thead th {
background: var(--primary);
color: #fff;
font-size: 13px;
font-weight: 600;
padding: 10px 8px;
text-align: center;
}
.consult-table tbody td {
font-size: 13px;
color: var(--text-primary);
padding: 11px 8px;
text-align: center;
border-bottom: 1px solid #f0f0f0;
background: #fff;
}
.consult-table tbody tr:last-child td {
border-bottom: none;
}
.consult-table thead th:first-child { width: 34%; }
.consult-table thead th:nth-child(2) { width: 22%; }
.consult-table thead th:nth-child(3) { width: 20%; }
.consult-table thead th:nth-child(4) { width: 24%; }
.is-abnormal { color: var(--danger); font-weight: 600; }
.consult-table tbody td:nth-child(2) { color: var(--danger); font-weight: 600; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">一键咨询</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- 化学检查 -->
<div class="category">
<div class="category__title">化学检查</div>
<!-- 血脂 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>血脂</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>高密度脂蛋白</td><td class="is-abnormal">0.90</td><td>mmol/L</td><td>1.04--1.66</td></tr>
</tbody>
</table>
</div>
<!-- 乙肝 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>乙肝</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>乙肝病毒表面抗体</td><td class="is-abnormal">弱阳性</td><td>/</td><td>阴性</td></tr>
</tbody>
</table>
</div>
<!-- 肝功 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>肝功</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>谷丙转氨酶</td><td class="is-abnormal">59</td><td>U/L</td><td>9--50</td></tr>
<tr><td>总胆红素</td><td class="is-abnormal">33.0</td><td>umol/L</td><td>3.42--23</td></tr>
<tr><td>直接胆红素</td><td class="is-abnormal">7.5</td><td>umol/L</td><td>0--6.84</td></tr>
<tr><td>间接胆红素</td><td class="is-abnormal">25.5</td><td>umol/L</td><td>3.4--16.1</td></tr>
</tbody>
</table>
</div>
<!-- 生化检查 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>生化检查</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>载脂蛋白-A1</td><td class="is-abnormal">0.88</td><td>g/L</td><td>1--1.6</td></tr>
</tbody>
</table>
</div>
</div>
<!-- 常规检查 -->
<div class="category">
<div class="category__title">常规检查</div>
<!-- 血常规 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>血常规</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>中性粒细胞比率</td><td class="is-abnormal">78.6</td><td>%</td><td>40--75</td></tr>
<tr><td>淋巴细胞比率</td><td class="is-abnormal">15.2</td><td>%</td><td>20--50</td></tr>
</tbody>
</table>
</div>
<!-- 尿常规 -->
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>尿常规</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>尿蛋白</td><td class="is-abnormal">弱阳性</td><td>/</td><td>阴性</td></tr>
</tbody>
</table>
</div>
</div>
<!-- 影像检查 -->
<div class="category">
<div class="category__title">影像检查</div>
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>彩超</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>肝胆脾胰双肾</td><td class="is-abnormal">轻度脂肪肝</td><td>/</td><td>未见异常</td></tr>
</tbody>
</table>
</div>
<div class="sub-section">
<div class="sub-section__header">
<div class="sub-section__name"><span class="sub-section__bar"></span>颈椎</div>
<a class="sub-section__link">专家库一键咨询&gt;</a>
</div>
<table class="consult-table">
<thead><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody>
<tr><td>颈椎 CT</td><td class="is-abnormal">生理曲度变直</td><td>/</td><td>未见异常</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>胃肠镜预约 - 预约日期</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center;
align-items: flex-start; min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%); padding-bottom: 120px; }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; margin-top: 6px; }
.summary-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.summary-list { display: flex; flex-direction: column; gap: 14px; }
.summary-item { display: flex; justify-content: space-between; gap: 12px; }
.summary-item__label { font-size: 13px; color: var(--text-muted); flex-shrink: 0; }
.summary-item__value { font-size: 14px; font-weight: 600; text-align: right; line-height: 1.6; }
.date-card {
background: #fff; border-radius: 20px; padding: 18px 16px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.calendar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.calendar-header__month { font-size: 17px; font-weight: 700; color: var(--text-primary); }
.calendar-header__nav { display: flex; gap: 8px; }
.calendar-header__btn {
width: 32px; height: 32px; border-radius: 10px; border: 1px solid #e8e8e8;
background: #fff; display: flex; align-items: center; justify-content: center;
cursor: pointer; font-size: 14px; color: var(--text-secondary); transition: all 0.2s;
}
.calendar-header__btn:hover { border-color: var(--primary); color: var(--primary); }
.calendar-header__btn:disabled { opacity: 0.3; pointer-events: none; }
.calendar-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 8px; }
.calendar-weekday { text-align: center; font-size: 12px; font-weight: 600; color: var(--text-muted); padding: 6px 0; }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.calendar-cell {
display: flex; flex-direction: column; align-items: center; justify-content: center;
height: 52px; border-radius: 12px; cursor: pointer; transition: all 0.2s;
border: 2px solid transparent; background: #f9fbfb; position: relative;
}
.calendar-cell:hover { border-color: var(--primary); }
.calendar-cell.selected { background: var(--primary); border-color: var(--primary); }
.calendar-cell.selected .calendar-cell__day,
.calendar-cell.selected .calendar-cell__slots { color: #fff; }
.calendar-cell.disabled { opacity: 0.3; pointer-events: none; background: #f5f5f5; }
.calendar-cell.empty { background: transparent; pointer-events: none; }
.calendar-cell__day { font-size: 15px; font-weight: 700; color: var(--text-primary); line-height: 1; }
.calendar-cell__slots { font-size: 10px; color: var(--primary-dark); font-weight: 600; margin-top: 2px; }
.calendar-cell__full { font-size: 10px; color: var(--text-muted); font-weight: 600; margin-top: 2px; }
.calendar-cell.today .calendar-cell__day { color: var(--primary-dark); }
.calendar-cell.today { border-color: rgba(19,194,194,0.3); }
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px;
display: flex; gap: 12px; padding-bottom: 16px;
}
.bottom-actions__btn { flex: 1; height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600; border: none; cursor: pointer; }
.bottom-actions__btn--ghost { background: #fff; color: var(--primary-dark); border: 2px solid #d0eded; }
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19,194,194,0.22);
}
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.42); z-index: 120;
display: flex; align-items: center; justify-content: center; padding: 24px;
}
.modal-overlay.hidden { display: none; }
.confirm-modal { width: 100%; background: #fff; border-radius: 24px; padding: 22px 20px 18px; box-shadow: 0 20px 50px rgba(0,0,0,0.16); }
.confirm-modal__title { font-size: 18px; font-weight: 700; text-align: center; margin-bottom: 8px; }
.confirm-modal__desc { font-size: 13px; color: var(--text-muted); text-align: center; line-height: 1.6; margin-bottom: 18px; }
.confirm-modal__info { background: #f7f9fb; border-radius: 16px; padding: 14px 16px; margin-bottom: 18px; }
.confirm-modal__row { display: flex; justify-content: space-between; align-items: center; font-size: 14px; color: var(--text-secondary); margin-bottom: 12px; }
.confirm-modal__row:last-child { margin-bottom: 0; }
.confirm-modal__value { font-size: 15px; font-weight: 700; color: var(--text-primary); }
.confirm-modal__value--highlight { color: var(--primary-dark); }
.confirm-modal__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.confirm-modal__btn { height: 44px; border-radius: 22px; font-size: 15px; font-weight: 600; border: none; cursor: pointer; }
.confirm-modal__btn--ghost { background: #f5f5f5; color: var(--text-secondary); }
.confirm-modal__btn--primary { background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff; }
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.9);
background: rgba(0,0,0,0.75); color: #fff; padding: 14px 22px; border-radius: 12px;
font-size: 13px; line-height: 1.6; text-align: center; z-index: 200;
opacity: 0; pointer-events: none; transition: all 0.25s ease; max-width: 280px;
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="gastro-questionnaire.html">&#8249;</a>
<span class="nav-title">胃肠镜预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="stepper">
<a class="stepper__item stepper__item--done" href="gastro-hospital.html"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></a>
<div class="stepper__line stepper__line--done"></div>
<a class="stepper__item stepper__item--done" href="gastro-questionnaire.html"><div class="stepper__dot">&#10003;</div><div class="stepper__label">填问卷</div></a>
<div class="stepper__line stepper__line--done"></div>
<div class="stepper__item stepper__item--active"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<div class="summary-card">
<div class="section-title">预约信息</div>
<div class="summary-list">
<div class="summary-item"><div class="summary-item__label">体检类型</div><div class="summary-item__value">胃肠镜体检</div></div>
<div class="summary-item"><div class="summary-item__label">体检医院</div><div class="summary-item__value">宁夏宝石花医院</div></div>
<div class="summary-item"><div class="summary-item__label">问卷状态</div><div class="summary-item__value" style="color:var(--primary-dark)">已完成</div></div>
</div>
</div>
<div class="date-card">
<div class="calendar-header">
<span class="calendar-header__month" id="calendarMonth"></span>
<div class="calendar-header__nav">
<button class="calendar-header__btn" id="prevMonth">&#8249;</button>
<button class="calendar-header__btn" id="nextMonth">&#8250;</button>
</div>
</div>
<div class="calendar-weekdays">
<span class="calendar-weekday"></span><span class="calendar-weekday"></span>
<span class="calendar-weekday"></span><span class="calendar-weekday"></span>
<span class="calendar-weekday"></span><span class="calendar-weekday"></span>
<span class="calendar-weekday"></span>
</div>
<div class="calendar-grid" id="calendarGrid"></div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="location.href='gastro-questionnaire.html'">上一步</button>
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="openConfirm()">确定预约</button>
</div>
<div class="modal-overlay hidden" id="confirmModal">
<div class="confirm-modal">
<div class="confirm-modal__title">确认预约信息</div>
<div class="confirm-modal__desc">请确认以下胃肠镜预约信息无误</div>
<div class="confirm-modal__info">
<div class="confirm-modal__row"><span>体检类型</span><span class="confirm-modal__value">胃肠镜体检</span></div>
<div class="confirm-modal__row"><span>体检医院</span><span class="confirm-modal__value">宁夏宝石花医院</span></div>
<div class="confirm-modal__row"><span>预约日期</span><span class="confirm-modal__value confirm-modal__value--highlight" id="confirmDate">--</span></div>
</div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--ghost" onclick="closeConfirm()">取消</button>
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="showSuccessToast()">确认预约</button>
</div>
</div>
</div>
<div class="toast" id="successToast">预约成功!请按时前往医院进行胃肠镜检查</div>
</div>
<script>
var today = new Date(2026, 2, 16);
var currentYear = today.getFullYear();
var currentMonth = today.getMonth();
var selectedDate = null;
var grid = document.getElementById('calendarGrid');
var monthLabel = document.getElementById('calendarMonth');
var prevBtn = document.getElementById('prevMonth');
var nextBtn = document.getElementById('nextMonth');
var toastTimer = null;
function getSlots(year, month, day) {
var d = new Date(year, month, day);
var dow = d.getDay();
if (d < new Date(today.getFullYear(), today.getMonth(), today.getDate())) return -1;
if (dow === 0 || dow === 6) return 0;
var seed = (day * 7 + month * 13) % 20;
if (seed < 3) return 0;
return seed + 2;
}
function renderCalendar() {
grid.innerHTML = '';
monthLabel.textContent = currentYear + '年' + (currentMonth + 1) + '月';
prevBtn.disabled = (currentYear === today.getFullYear() && currentMonth <= today.getMonth());
var firstDay = new Date(currentYear, currentMonth, 1);
var startDow = firstDay.getDay();
startDow = startDow === 0 ? 6 : startDow - 1;
var daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
for (var i = 0; i < startDow; i++) {
var empty = document.createElement('div');
empty.className = 'calendar-cell empty';
grid.appendChild(empty);
}
for (var day = 1; day <= daysInMonth; day++) {
(function(d) {
var cell = document.createElement('div');
var slots = getSlots(currentYear, currentMonth, d);
var isToday = (currentYear === today.getFullYear() && currentMonth === today.getMonth() && d === today.getDate());
var isDisabled = slots <= 0;
var isFull = slots === 0;
var isPast = slots === -1;
var cls = 'calendar-cell';
if (isToday) cls += ' today';
if (isDisabled) cls += ' disabled';
if (selectedDate && selectedDate.y === currentYear && selectedDate.m === currentMonth && selectedDate.d === d) cls += ' selected';
cell.className = cls;
var slotsHtml = '';
if (isPast) slotsHtml = '';
else if (isFull) slotsHtml = '<span class="calendar-cell__full">不可约</span>';
else if (slots > 0) slotsHtml = '<span class="calendar-cell__slots">余' + slots + '</span>';
cell.innerHTML = '<span class="calendar-cell__day">' + d + '</span>' + slotsHtml;
if (!isDisabled) {
cell.addEventListener('click', function() {
selectedDate = { y: currentYear, m: currentMonth, d: d };
renderCalendar();
});
}
grid.appendChild(cell);
})(day);
}
}
prevBtn.addEventListener('click', function() {
currentMonth--;
if (currentMonth < 0) { currentMonth = 11; currentYear--; }
renderCalendar();
});
nextBtn.addEventListener('click', function() {
currentMonth++;
if (currentMonth > 11) { currentMonth = 0; currentYear++; }
renderCalendar();
});
selectedDate = { y: today.getFullYear(), m: today.getMonth(), d: today.getDate() };
renderCalendar();
function openConfirm() {
if (!selectedDate) return;
document.getElementById('confirmDate').textContent = selectedDate.y + '年' + (selectedDate.m + 1) + '月' + selectedDate.d + '日';
document.getElementById('confirmModal').classList.remove('hidden');
}
function closeConfirm() { document.getElementById('confirmModal').classList.add('hidden'); }
function showSuccessToast() {
closeConfirm();
var toast = document.getElementById('successToast');
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(function() { toast.classList.remove('show'); }, 2200);
}
</script>
</body>
</html>
@@ -0,0 +1,224 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>胃肠镜预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.nav-right {
position: absolute; right: 16px; font-size: 13px; color: var(--primary);
font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 4px;
}
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%); padding-bottom: 120px; }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; margin-top: 6px; }
.hospital-card {
background: #fff; border-radius: 18px; padding: 16px; margin-bottom: 12px;
border: 2px solid transparent; box-shadow: var(--shadow); cursor: pointer;
display: flex; align-items: center; justify-content: space-between; transition: all 0.25s ease;
}
.hospital-card.active { border-color: var(--primary); background: var(--primary-light); }
.hospital-card__info { display: flex; align-items: center; gap: 12px; }
.hospital-card__icon {
width: 42px; height: 42px; border-radius: 14px;
background: linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%);
display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0;
}
.hospital-card__name { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 2px; }
.hospital-card__addr { font-size: 12px; color: var(--text-muted); }
.radio-dot {
width: 22px; height: 22px; border-radius: 50%; border: 2px solid #d9d9d9;
display: flex; align-items: center; justify-content: center; transition: all 0.25s ease; flex-shrink: 0;
}
.hospital-card.active .radio-dot { border-color: var(--primary); background: var(--primary); }
.radio-dot::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: #fff; opacity: 0; transition: opacity 0.25s ease; }
.hospital-card.active .radio-dot::after { opacity: 1; }
.bottom-actions { position: absolute; left: 16px; right: 16px; bottom: 18px; padding-bottom: 16px; }
.bottom-actions__btn {
width: 100%; height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600;
border: none; cursor: pointer; background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.45); z-index: 100;
display: flex; align-items: center; justify-content: center;
}
.modal-overlay.hidden { display: none; }
.modal-card { width: 340px; background: #fff; border-radius: 24px; padding: 24px 20px; box-shadow: 0 20px 60px rgba(0,0,0,0.18); }
.modal-card__title { font-size: 18px; font-weight: 700; text-align: center; margin-bottom: 6px; }
.modal-card__sub { font-size: 13px; color: var(--text-muted); text-align: center; margin-bottom: 20px; }
.modal-field { display: flex; justify-content: space-between; align-items: center; padding: 14px 0; border-bottom: 1px solid #f0f0f0; }
.modal-field:last-of-type { border-bottom: none; }
.modal-field__label { font-size: 14px; color: var(--text-muted); }
.modal-field__value { font-size: 14px; color: var(--text-primary); font-weight: 600; }
.marriage-toggle { display: inline-flex; background: #f5f5f5; border-radius: 20px; overflow: hidden; }
.marriage-toggle__btn {
padding: 6px 16px; font-size: 13px; font-weight: 600; border: none;
background: transparent; color: var(--text-muted); cursor: pointer; border-radius: 20px; transition: all 0.2s ease;
}
.marriage-toggle__btn.active { background: var(--primary); color: #fff; }
.modal-confirm-btn {
margin-top: 20px; width: 100%; height: 44px; border-radius: 22px; font-size: 15px; font-weight: 600;
border: none; cursor: pointer; background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 8px 20px rgba(19, 194, 194, 0.2);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="booking-type.html">&#8249;</a>
<span class="nav-title">胃肠镜预约</span>
<span class="nav-right" onclick="openModal()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
个人信息
</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="stepper">
<a class="stepper__item stepper__item--active" href="gastro-hospital.html"><div class="stepper__dot">1</div><div class="stepper__label">选择医院</div></a>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">2</div><div class="stepper__label">填问卷</div></div>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<div class="section-title">选择医院</div>
<div class="hospital-card active" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">宁夏宝石花医院</div><div class="hospital-card__addr">宁夏银川市</div></div>
</div>
<div class="radio-dot"></div>
</div>
<div class="hospital-card" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">长庆星龙园医院</div><div class="hospital-card__addr">陕西西安市</div></div>
</div>
<div class="radio-dot"></div>
</div>
<div class="hospital-card" onclick="selectHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div><div class="hospital-card__name">西安长庆医院</div><div class="hospital-card__addr">陕西西安市</div></div>
</div>
<div class="radio-dot"></div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn" onclick="location.href='gastro-questionnaire.html'">下一步</button>
</div>
<div class="modal-overlay hidden" id="personModal">
<div class="modal-card">
<div class="modal-card__title">确认个人信息</div>
<div class="modal-card__sub">请核对以下信息,确认无误后继续预约</div>
<div class="modal-field"><span class="modal-field__label">姓名</span><span class="modal-field__value">刘韵琴</span></div>
<div class="modal-field"><span class="modal-field__label">单位</span><span class="modal-field__value">长庆油田第一采油厂</span></div>
<div class="modal-field"><span class="modal-field__label">身份证号</span><span class="modal-field__value">6101021994********</span></div>
<div class="modal-field"><span class="modal-field__label">性别</span><span class="modal-field__value"></span></div>
<div class="modal-field">
<span class="modal-field__label">婚姻状况</span>
<div class="marriage-toggle">
<button class="marriage-toggle__btn active" onclick="toggleMarriage(this)">已婚</button>
<button class="marriage-toggle__btn" onclick="toggleMarriage(this)">未婚</button>
</div>
</div>
<button class="modal-confirm-btn" onclick="closeModal()">确认</button>
</div>
</div>
</div>
<script>
if (new URLSearchParams(window.location.search).get('from') === 'booking-type') {
document.getElementById('personModal').classList.remove('hidden');
}
function selectHospital(el) {
document.querySelectorAll('.hospital-card').forEach(c => c.classList.remove('active'));
el.classList.add('active');
}
function openModal() { document.getElementById('personModal').classList.remove('hidden'); }
function closeModal() { document.getElementById('personModal').classList.add('hidden'); }
function toggleMarriage(btn) {
document.querySelectorAll('.marriage-toggle__btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
}
</script>
</body>
</html>
@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>胃肠镜预约 - 填问卷</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center;
align-items: flex-start; min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%); padding-bottom: 120px; }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.stepper { display: flex; align-items: center; justify-content: center; margin-bottom: 18px; padding: 16px 0; }
.stepper__item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }
.stepper__item--future { cursor: default; pointer-events: none; opacity: 0.45; }
.stepper__dot {
width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; background: #e8e8e8; color: #bfbfbf; transition: all 0.25s;
}
.stepper__item--done .stepper__dot { background: var(--primary); color: #fff; }
.stepper__item--active { cursor: default; pointer-events: none; }
.stepper__item--active .stepper__dot {
width: 34px; height: 34px; font-size: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
box-shadow: 0 0 0 6px rgba(19,194,194,0.2);
}
.stepper__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; transition: all 0.2s; }
.stepper__item--done .stepper__label { color: var(--primary-dark); }
.stepper__item--active .stepper__label {
color: var(--primary-dark); font-weight: 700; font-size: 13px;
background: var(--primary-light); padding: 2px 10px; border-radius: 10px;
}
.stepper__item--done:hover .stepper__dot { transform: scale(1.12); box-shadow: 0 0 0 4px rgba(19,194,194,0.15); }
.stepper__item--done:hover .stepper__label { text-decoration: underline; text-underline-offset: 3px; }
.stepper__line { width: 44px; height: 3px; background: #e8e8e8; margin: 0 8px; margin-bottom: 22px; border-radius: 2px; }
.stepper__line--done { background: var(--primary); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.section-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 20px; }
.q-card {
background: #fff; border-radius: 18px; padding: 18px; margin-bottom: 14px;
box-shadow: var(--shadow);
}
.q-card__index {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%; background: var(--primary);
color: #fff; font-size: 11px; font-weight: 700; margin-right: 8px; flex-shrink: 0;
}
.q-card__label {
font-size: 15px; font-weight: 600; color: var(--text-primary);
display: flex; align-items: center; margin-bottom: 14px;
}
.q-options { display: flex; gap: 12px; }
.q-option {
flex: 1; height: 44px; border-radius: 22px; border: 2px solid #e8e8e8;
background: #fff; font-size: 14px; font-weight: 600; color: var(--text-secondary);
cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center;
}
.q-option.active { border-color: var(--primary); background: var(--primary-light); color: var(--primary-dark); }
.bottom-actions {
position: absolute; left: 16px; right: 16px; bottom: 18px; padding-bottom: 16px;
display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.bottom-actions__btn {
height: 48px; border-radius: 24px; font-size: 15px; font-weight: 600; border: none; cursor: pointer;
}
.bottom-actions__btn--ghost {
background: #fff; color: var(--primary-dark); border: 2px solid #d0eded;
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="gastro-hospital.html">&#8249;</a>
<span class="nav-title">胃肠镜预约</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="stepper">
<a class="stepper__item stepper__item--done" href="gastro-hospital.html"><div class="stepper__dot">&#10003;</div><div class="stepper__label">选择医院</div></a>
<div class="stepper__line stepper__line--done"></div>
<div class="stepper__item stepper__item--active"><div class="stepper__dot">2</div><div class="stepper__label">填问卷</div></div>
<div class="stepper__line"></div>
<div class="stepper__item stepper__item--future"><div class="stepper__dot">3</div><div class="stepper__label">预约日期</div></div>
</div>
<div class="section-title">胃肠镜检查问卷</div>
<div class="section-desc">请如实填写以下信息,以便医生评估检查方案</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">1</span>是否使用阿司匹林?</div>
<div class="q-options">
<button class="q-option" onclick="toggleOption(this)"></button>
<button class="q-option active" onclick="toggleOption(this)"></button>
</div>
</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">2</span>是否有心脏疾病?</div>
<div class="q-options">
<button class="q-option" onclick="toggleOption(this)"></button>
<button class="q-option active" onclick="toggleOption(this)"></button>
</div>
</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">3</span>是否有肺部疾病?</div>
<div class="q-options">
<button class="q-option" onclick="toggleOption(this)"></button>
<button class="q-option active" onclick="toggleOption(this)"></button>
</div>
</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">4</span>是否有哮喘?</div>
<div class="q-options">
<button class="q-option" onclick="toggleOption(this)"></button>
<button class="q-option active" onclick="toggleOption(this)"></button>
</div>
</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">5</span>胸部是否检查?</div>
<div class="q-options">
<button class="q-option active" onclick="toggleOption(this)"></button>
<button class="q-option" onclick="toggleOption(this)"></button>
</div>
</div>
<div class="q-card">
<div class="q-card__label"><span class="q-card__index">6</span>血液是否检查?</div>
<div class="q-options">
<button class="q-option active" onclick="toggleOption(this)"></button>
<button class="q-option" onclick="toggleOption(this)"></button>
</div>
</div>
</div>
</div>
<div class="bottom-actions">
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="location.href='gastro-hospital.html'">上一步</button>
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="location.href='gastro-date.html'">下一步</button>
</div>
</div>
<script>
function toggleOption(btn) {
var group = btn.parentElement;
group.querySelectorAll('.q-option').forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
}
</script>
</body>
</html>
@@ -0,0 +1,253 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>到院体检</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 26px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 160px, #f7f9fb 100%);
padding: 16px;
padding-bottom: 110px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.hero-card {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 24px;
padding: 22px 18px;
color: #fff;
box-shadow: 0 12px 28px rgba(19, 194, 194, 0.22);
}
.hero-card__badge {
display: inline-flex;
align-items: center;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255,255,255,0.18);
font-size: 12px;
font-weight: 600;
margin-bottom: 14px;
}
.hero-card__title {
font-size: 24px;
font-weight: 700;
margin-bottom: 8px;
}
.hero-card__desc {
font-size: 13px;
line-height: 1.7;
opacity: 0.92;
}
.detail-card,
.notice-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 18px 16px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
}
.detail-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: flex-start;
}
.detail-item__label {
font-size: 13px;
color: var(--text-muted);
flex-shrink: 0;
}
.detail-item__value {
font-size: 14px;
color: var(--text-primary);
font-weight: 600;
text-align: right;
line-height: 1.6;
}
.notice-card__text {
font-size: 14px;
line-height: 1.9;
color: var(--text-secondary);
}
.route-card {
margin-top: 14px;
padding: 16px;
border-radius: 20px;
background: linear-gradient(135deg, #ffffff 0%, #f5fffe 100%);
border: 1px solid #e3f7f5;
box-shadow: var(--shadow);
}
.route-card__title {
font-size: 15px;
font-weight: 700;
margin-bottom: 8px;
}
.route-card__desc {
font-size: 13px;
line-height: 1.7;
color: var(--text-secondary);
}
.bottom-action {
position: absolute;
left: 16px;
right: 16px;
bottom: 18px;
padding-bottom: 16px;
}
.bottom-action__btn {
width: 100%;
height: 48px;
border: none;
border-radius: 24px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
font-size: 15px;
font-weight: 600;
box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
cursor: pointer;
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回至: my-appointments.html -->
<a class="nav-back" href="my-appointments.html"></a>
<span class="nav-title">到院体检</span>
</div>
<div class="scroll-content">
<div class="hero-card">
<div class="hero-card__badge">到院签到提醒</div>
<div class="hero-card__title">常规体检</div>
<div class="hero-card__desc">请根据预约信息前往对应医院,完成签到后按体检单顺序进行检查,异常情况可联系导检台。</div>
</div>
<div class="detail-card">
<div class="section-title">预约详情</div>
<div class="detail-list">
<div class="detail-item">
<div class="detail-item__label">体检医院</div>
<div class="detail-item__value">宁夏宝石花医院</div>
</div>
<div class="detail-item">
<div class="detail-item__label">预约日期</div>
<div class="detail-item__value">2026-03-23</div>
</div>
<div class="detail-item">
<div class="detail-item__label">体检地址</div>
<div class="detail-item__value">凤城五路与开元路十字东南角</div>
</div>
</div>
</div>
<div class="notice-card">
<div class="section-title">体检须知</div>
<div class="notice-card__text">体检前一天请清淡饮食,勿饮酒,勿劳累;体检当天请空腹,禁食。</div>
</div>
<div class="route-card">
<div class="route-card__title">到院建议</div>
<div class="route-card__desc">建议您提前 15 分钟到院,便于完成签到、核验身份与领取体检单,避免高峰时段排队等待。</div>
</div>
</div>
<div class="bottom-action">
<!-- [交互] 点击跳转至: my-appointments.html -->
<button class="bottom-action__btn" onclick="window.location.href='my-appointments.html'">返回我的预约</button>
</div>
</div>
</body>
</html>
@@ -0,0 +1,280 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康体检</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
.phone-shell {
width: 390px;
min-height: 844px;
background: #f7f9fb;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: #1a1a1a;
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #f3fbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.hero-card {
background: linear-gradient(135deg, #ffffff 0%, #f3fffe 100%);
border: 1px solid #d9f7f3;
border-radius: 20px;
padding: 18px 16px;
box-shadow: 0 6px 18px rgba(20, 180, 176, 0.08);
position: relative;
overflow: hidden;
}
.hero-card::after {
content: '';
position: absolute;
right: -36px;
top: -36px;
width: 120px;
height: 120px;
border-radius: 50%;
background: rgba(19, 194, 194, 0.08);
}
.hero-card__badge {
display: inline-flex;
align-items: center;
gap: 6px;
height: 28px;
padding: 0 10px;
border-radius: 14px;
background: rgba(250, 173, 20, 0.12);
color: #d48806;
font-size: 12px;
font-weight: 600;
margin-bottom: 12px;
position: relative;
z-index: 1;
}
.hero-card__title {
font-size: 22px;
line-height: 1.2;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 10px;
position: relative;
z-index: 1;
}
.hero-card__desc {
font-size: 14px;
line-height: 1.7;
color: #666;
position: relative;
z-index: 1;
}
.pending-card {
margin-top: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 18px;
padding: 16px;
color: #fff;
box-shadow: 0 10px 24px rgba(19, 194, 194, 0.22);
}
.pending-card__label { font-size: 13px; opacity: 0.9; margin-bottom: 8px; }
.pending-card__value { font-size: 28px; font-weight: 800; line-height: 1; }
.pending-card__meta { margin-top: 10px; font-size: 12px; opacity: 0.86; }
.section-title { font-size: 16px; font-weight: 700; color: #1a1a1a; margin: 22px 2px 12px; }
.feature-list { display: flex; flex-direction: column; gap: 12px; }
.feature-card {
background: #fff; border-radius: 18px; padding: 16px; display: flex; align-items: center; gap: 14px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05); border: 1px solid #f0f0f0; cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.feature-card:active { transform: scale(0.985); }
.feature-card__icon {
width: 52px; height: 52px; border-radius: 16px; display: flex; align-items: center;
justify-content: center; flex-shrink: 0;
}
.feature-card__content { flex: 1; min-width: 0; }
.feature-card__title { font-size: 16px; font-weight: 700; color: #1a1a1a; margin-bottom: 4px; }
.feature-card__desc { font-size: 12px; line-height: 1.6; color: #8c8c8c; }
.feature-card__action {
margin-top: 10px; display: inline-flex; align-items: center; justify-content: center; height: 28px;
padding: 0 12px; border-radius: 14px; background: #f5f7fa; color: #13c2c2; font-size: 12px; font-weight: 600;
}
.feature-card__arrow { font-size: 18px; color: #c6c6c6; flex-shrink: 0; }
.quick-grid {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 12px;
}
.quick-card {
background: #fff; border-radius: 16px; padding: 16px 14px; min-height: 118px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05); border: 1px solid #f0f0f0;
position: relative; overflow: hidden;
}
.quick-card::after {
content: ''; position: absolute; right: -20px; bottom: -20px; width: 72px; height: 72px;
border-radius: 50%; background: rgba(19, 194, 194, 0.08);
}
.quick-card__icon {
width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center;
justify-content: center; margin-bottom: 12px;
}
.quick-card__title {
font-size: 15px; font-weight: 700; color: #1a1a1a; margin-bottom: 6px; position: relative; z-index: 1;
}
.quick-card__desc {
font-size: 12px; line-height: 1.6; color: #8c8c8c; position: relative; z-index: 1;
}
.bottom-spacer { height: 28px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<span class="nav-back" onclick="history.back()"></span>
<span class="nav-title">健康体检</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="hero-card">
<div class="hero-card__badge">
<span>📋</span>
<span>年度体检计划</span>
</div>
<div class="hero-card__title">健康体检</div>
<div class="hero-card__desc">本年度暂未开展体检计划,请耐心等待</div>
<div class="pending-card">
<div class="pending-card__label">待查看的体检报告</div>
<div class="pending-card__value">2 份</div>
<div class="pending-card__meta">历史报告已生成,可前往体检报告查看既往体检记录与指标摘要。</div>
</div>
</div>
<div class="section-title">体检服务</div>
<div class="feature-list">
<!-- [交互] 点击跳转至: booking-type.html -->
<div class="feature-card" onclick="location.href='booking-type.html'">
<div class="feature-card__icon" style="background:#e6fffb; color:#13c2c2;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="17" rx="2"/><line x1="16" y1="2.5" x2="16" y2="6"/><line x1="8" y1="2.5" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M8 14h3"/><path d="M8 17h5"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">体检预约</div>
<div class="feature-card__desc">查看预约入口与年度体检开放状态</div>
<span class="feature-card__action">体检预约</span>
</div>
<span class="feature-card__arrow"></span>
</div>
<!-- [交互] 点击跳转至: my-appointments.html -->
<div class="feature-card" onclick="location.href='my-appointments.html'">
<div class="feature-card__icon" style="background:#f6ffed; color:#52c41a;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">我的预约</div>
<div class="feature-card__desc">查看历史预约记录与当前预约状态</div>
<span class="feature-card__action">查看预约</span>
</div>
<span class="feature-card__arrow"></span>
</div>
<!-- [交互] 点击跳转至: reports.html -->
<div class="feature-card" onclick="location.href='reports.html'">
<div class="feature-card__icon" style="background:#fff7e6; color:#fa8c16;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">体检报告</div>
<div class="feature-card__desc">查看历年体检报告与 AI 解读入口</div>
<span class="feature-card__action">查看报告</span>
</div>
<span class="feature-card__arrow"></span>
</div>
</div>
<div class="section-title">辅助入口</div>
<div class="quick-grid">
<!-- [交互] 操作流程讲解入口,当前为静态说明卡片 -->
<div class="quick-card">
<div class="quick-card__icon" style="background:#eef9ff; color:#1677ff;">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 8v4l2.5 2.5"/></svg>
</div>
<div class="quick-card__title">操作流程讲解</div>
<div class="quick-card__desc">查看体检计划开放后,预约、到院、报告查询等关键流程说明。</div>
</div>
<!-- [交互] 一键咨询入口,当前为静态说明卡片 -->
<div class="quick-card">
<div class="quick-card__icon" style="background:#f9f0ff; color:#722ed1;">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 9h8"/><path d="M8 13h5"/></svg>
</div>
<div class="quick-card__title">一键咨询</div>
<div class="quick-card__desc">对体检开放时间、项目内容和报告指标进行咨询。</div>
</div>
</div>
<div class="bottom-spacer"></div>
</div>
</div>
</div>
</body>
</html>
+392
View File
@@ -0,0 +1,392 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康体检</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
.phone-shell {
width: 390px;
min-height: 844px;
background: #f7f9fb;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: #1a1a1a;
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #f3fbfb 0%, #f7f9fb 160px, #f7f9fb 100%);
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
.hero-card {
background: linear-gradient(135deg, #ffffff 0%, #f3fffe 100%);
border: 1px solid #e3f6f4;
border-radius: 20px;
padding: 18px 16px;
box-shadow: 0 6px 18px rgba(20, 180, 176, 0.08);
}
.hero-card__badge {
display: inline-flex;
align-items: center;
gap: 6px;
height: 28px;
padding: 0 10px;
border-radius: 14px;
background: rgba(255, 170, 0, 0.12);
color: #d48806;
font-size: 12px;
font-weight: 600;
margin-bottom: 12px;
}
.hero-card__title {
font-size: 22px;
line-height: 1.2;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 10px;
}
.hero-card__desc {
font-size: 14px;
line-height: 1.7;
color: #666;
}
.fee-card {
margin-top: 14px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 18px;
padding: 16px;
color: #fff;
box-shadow: 0 10px 24px rgba(19, 194, 194, 0.22);
}
.fee-card__label {
font-size: 13px;
opacity: 0.9;
margin-bottom: 8px;
}
.fee-card__value {
font-size: 30px;
font-weight: 800;
line-height: 1;
letter-spacing: 0.5px;
}
.fee-card__meta {
margin-top: 10px;
font-size: 12px;
opacity: 0.86;
}
.section-title {
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
margin: 22px 2px 12px;
}
.feature-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.feature-card {
background: #fff;
border-radius: 18px;
padding: 16px;
display: flex;
align-items: center;
gap: 14px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05);
border: 1px solid #f0f0f0;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.feature-card:active { transform: scale(0.985); }
.feature-card__icon {
width: 52px;
height: 52px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.feature-card__content { flex: 1; min-width: 0; }
.feature-card__title {
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4px;
}
.feature-card__desc {
font-size: 12px;
line-height: 1.6;
color: #8c8c8c;
}
.feature-card__action {
margin-top: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
height: 28px;
padding: 0 12px;
border-radius: 14px;
background: #f5f7fa;
color: #13c2c2;
font-size: 12px;
font-weight: 600;
}
.feature-card__arrow {
font-size: 18px;
color: #c6c6c6;
flex-shrink: 0;
}
.quick-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-top: 12px;
}
.quick-card {
background: #fff;
border-radius: 16px;
padding: 16px 14px;
min-height: 118px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05);
border: 1px solid #f0f0f0;
cursor: pointer;
transition: transform 0.15s ease;
position: relative;
overflow: hidden;
}
.quick-card:active { transform: scale(0.985); }
.quick-card::after {
content: '';
position: absolute;
right: -20px;
bottom: -20px;
width: 72px;
height: 72px;
border-radius: 50%;
background: rgba(19, 194, 194, 0.08);
}
.quick-card__icon {
width: 40px;
height: 40px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.quick-card__title {
font-size: 15px;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 6px;
position: relative;
z-index: 1;
}
.quick-card__desc {
font-size: 12px;
line-height: 1.6;
color: #8c8c8c;
position: relative;
z-index: 1;
}
.tips-card {
margin-top: 16px;
background: #fff;
border-radius: 18px;
padding: 16px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05);
border: 1px solid #f0f0f0;
}
.tips-card__title {
font-size: 15px;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 10px;
}
.tips-card__list {
display: flex;
flex-direction: column;
gap: 8px;
}
.tips-card__item {
font-size: 13px;
line-height: 1.6;
color: #666;
display: flex;
gap: 8px;
}
.tips-card__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #13c2c2;
margin-top: 7px;
flex-shrink: 0;
}
.bottom-spacer { height: 28px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<span class="nav-back" onclick="location.href='../home/index.html'"></span>
<span class="nav-title">健康体检</span>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="hero-card">
<div class="hero-card__badge">
<span>📋</span>
<span>年度体检计划</span>
</div>
<!-- <div class="hero-card__title">健康体检</div>-->
<div class="hero-card__desc">本单位已开展体检计划,请尽快进行体检预约并合理安排时间前往医院进行体检。</div>
<div class="fee-card">
<div class="fee-card__label">本年度体检费用(元)</div>
<div class="fee-card__value">1500.00</div>
<!-- <div class="fee-card__meta">费用由单位统一配置,预约时自动关联体检项目。</div>-->
</div>
</div>
<div class="section-title">体检服务</div>
<div class="feature-list">
<!-- [交互] 点击跳转至: booking-type.html -->
<div class="feature-card" onclick="location.href='booking-type.html'">
<div class="feature-card__icon" style="background:#e6fffb; color:#13c2c2;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="17" rx="2"/><line x1="16" y1="2.5" x2="16" y2="6"/><line x1="8" y1="2.5" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M8 14h3"/><path d="M8 17h5"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">体检预约</div>
<div class="feature-card__desc">选择体检医院和体检项目,预约体检日期</div>
<span class="feature-card__action">体检预约</span>
</div>
<span class="feature-card__arrow"></span>
</div>
<!-- [交互] 点击跳转至: my-appointments.html -->
<div class="feature-card" onclick="location.href='my-appointments.html'">
<div class="feature-card__icon" style="background:#f6ffed; color:#52c41a;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">我的预约</div>
<div class="feature-card__desc">查看/取消体检预约</div>
<span class="feature-card__action">查看预约</span>
</div>
<span class="feature-card__arrow"></span>
</div>
<!-- [交互] 点击跳转至: reports.html -->
<div class="feature-card" onclick="location.href='reports.html'">
<div class="feature-card__icon" style="background:#fff7e6; color:#fa8c16;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="feature-card__content">
<div class="feature-card__title">体检报告</div>
<div class="feature-card__desc">查看报告/ 解读结果</div>
<span class="feature-card__action">查看报告</span>
</div>
<span class="feature-card__arrow"></span>
</div>
</div>
<div class="section-title">辅助入口</div>
<div class="quick-grid">
<!-- [交互] 操作流程讲解入口,后续可跳转至体检流程说明页 -->
<div class="quick-card">
<div class="quick-card__icon" style="background:#eef9ff; color:#1677ff;">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 8v4l2.5 2.5"/></svg>
</div>
<div class="quick-card__title">操作流程讲解</div>
<div class="quick-card__desc">查看预约、到院、报告查询等关键流程说明。</div>
</div>
<!-- [交互] 一键咨询入口,点击跳转至 consult.html -->
<div class="quick-card" onclick="location.href='consult.html'">
<div class="quick-card__icon" style="background:#f9f0ff; color:#722ed1;">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 9h8"/><path d="M8 13h5"/></svg>
</div>
<div class="quick-card__title">一键咨询</div>
<div class="quick-card__desc">对体检项目、报告指标和异常结果发起咨询。</div>
</div>
</div>
<!-- <div class="tips-card">-->
<!-- <div class="tips-card__title">体检提醒</div>-->
<!-- <div class="tips-card__list">-->
<!-- <div class="tips-card__item"><span class="tips-card__dot"></span><span>预约成功后,请按预约日期前往对应医院完成检查。</span></div>-->
<!-- <div class="tips-card__item"><span class="tips-card__dot"></span><span>空腹类项目建议体检前一晚清淡饮食,避免饮酒和熬夜。</span></div>-->
<!-- <div class="tips-card__item"><span class="tips-card__dot"></span><span>报告生成后可在线查看,并结合解读结果关注异常指标。</span></div>-->
<!-- </div>-->
<!-- </div>-->
<div class="bottom-spacer"></div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-bg: #f6ffed;
--success-text: #52c41a;
--warning-bg: #fff7e6;
--warning-text: #fa8c16;
--danger-bg: #fff1f0;
--danger-text: #ff4d4f;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--border: #e8f3f3;
--shadow: 0 10px 24px rgba(19, 194, 194, 0.06);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #f0fbfb 0%, #f7f9fb 140px, #f7f9fb 100%);
padding: 14px 16px 20px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.tabs {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 6px;
background: rgba(255,255,255,0.92); border-radius: 18px;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.08);
position: sticky; top: 0; z-index: 5; backdrop-filter: blur(10px);
}
.tabs__item {
height: 36px; border: none; border-radius: 14px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
text-decoration: none; display: flex; align-items: center; justify-content: center;
}
.tabs__item--active {
background: var(--primary-light); color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.summary-bar {
margin: 14px 0 12px; padding: 16px; border-radius: 20px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 12px 24px rgba(19, 194, 194, 0.2);
}
.summary-bar__title { font-size: 18px; font-weight: 700; margin-bottom: 6px; }
.summary-bar__desc { font-size: 13px; line-height: 1.7; opacity: 0.92; }
.appointment-list { display: flex; flex-direction: column; gap: 12px; }
.appointment-card {
background: #fff; border-radius: 20px; padding: 16px;
border: 1px solid rgba(19, 194, 194, 0.06); box-shadow: var(--shadow);
}
.appointment-card[data-status="done"],
.appointment-card[data-status="cancelled"] { opacity: 0.94; }
.appointment-card__top { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 12px; }
.appointment-card__title { font-size: 16px; font-weight: 700; color: var(--text-primary); }
.appointment-card__status { flex-shrink: 0; padding: 5px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.appointment-card__status--pending { background: var(--warning-bg); color: var(--warning-text); }
.appointment-card__status--done { background: var(--success-bg); color: var(--success-text); }
.appointment-card__status--cancelled { background: var(--danger-bg); color: var(--danger-text); }
.appointment-card__meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; margin-bottom: 12px; }
.meta-item { background: #f9fbfb; border-radius: 14px; padding: 10px 12px; border: 1px solid #f0f5f5; }
.meta-item__label { font-size: 11px; color: var(--text-muted); margin-bottom: 5px; }
.meta-item__value { font-size: 13px; color: var(--text-primary); font-weight: 600; line-height: 1.5; }
.appointment-card__progress { margin-bottom: 14px; }
.progress-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 12px; color: var(--text-secondary); }
.progress-bar { height: 8px; border-radius: 999px; background: #eef3f3; overflow: hidden; }
.progress-bar__fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #13c2c2 0%, #5cdbd3 100%); }
.appointment-card__actions { display: flex; gap: 10px; }
.appointment-card__btn {
flex: 1; height: 40px; border-radius: 20px; font-size: 14px; font-weight: 600; border: none; cursor: pointer;
}
.appointment-card__btn--ghost { background: #f5f7fa; color: var(--text-secondary); border: 1px solid #ebeff2; }
.appointment-card__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 8px 20px rgba(19, 194, 194, 0.18);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">我的预约</span>
</div>
<div class="scroll-content">
<div class="tabs">
<a class="tabs__item tabs__item--active" href="my-appointments-all.html">全部</a>
<a class="tabs__item" href="my-appointments.html">待体检</a>
<a class="tabs__item" href="my-appointments-done.html">已完成</a>
<a class="tabs__item" href="my-appointments-cancelled.html">已取消</a>
</div>
<div class="appointment-list">
<!-- 待体检 -->
<div class="appointment-card" data-status="pending">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--pending">待体检</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-23</div></div>
</div>
<div class="appointment-card__progress">
<div class="progress-head"><span>体检进度</span><span>5/26</span></div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 19.2%;"></div></div>
</div>
</div>
<!-- 已完成 -->
<div class="appointment-card" data-status="done">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--done">已完成</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-23</div></div>
</div>
<div class="appointment-card__progress">
<div class="progress-head"><span>体检进度</span><span>26/26</span></div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 100%;"></div></div>
</div>
</div>
<!-- 已取消 -->
<div class="appointment-card" data-status="cancelled">
<div class="appointment-card__top">
<div class="appointment-card__title">胃肠镜体检</div>
<div class="appointment-card__status appointment-card__status--cancelled">已取消</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-10</div></div>
</div>
</div>
<!-- 待体检 -->
<div class="appointment-card" data-status="pending">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--pending">待体检</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-23</div></div>
</div>
<div class="appointment-card__progress">
<div class="progress-head"><span>体检进度</span><span>0/26</span></div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 0%;"></div></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,150 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-bg: #f6ffed;
--success-text: #52c41a;
--warning-bg: #fff7e6;
--warning-text: #fa8c16;
--danger-bg: #fff1f0;
--danger-text: #ff4d4f;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--border: #e8f3f3;
--shadow: 0 10px 24px rgba(19, 194, 194, 0.06);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #f0fbfb 0%, #f7f9fb 140px, #f7f9fb 100%);
padding: 14px 16px 20px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.tabs {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 6px;
background: rgba(255,255,255,0.92); border-radius: 18px;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.08);
position: sticky; top: 0; z-index: 5; backdrop-filter: blur(10px);
}
.tabs__item {
height: 36px; border: none; border-radius: 14px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
text-decoration: none; display: flex; align-items: center; justify-content: center;
}
.tabs__item--active {
background: var(--primary-light); color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.summary-bar {
margin: 14px 0 12px; padding: 16px; border-radius: 20px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 12px 24px rgba(19, 194, 194, 0.2);
}
.summary-bar__title { font-size: 18px; font-weight: 700; margin-bottom: 6px; }
.summary-bar__desc { font-size: 13px; line-height: 1.7; opacity: 0.92; }
.appointment-list { display: flex; flex-direction: column; gap: 12px; }
.appointment-card {
background: #fff; border-radius: 20px; padding: 16px;
border: 1px solid rgba(19, 194, 194, 0.06); box-shadow: var(--shadow); opacity: 0.94;
}
.appointment-card__top { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 12px; }
.appointment-card__title { font-size: 16px; font-weight: 700; color: var(--text-primary); }
.appointment-card__status { flex-shrink: 0; padding: 5px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.appointment-card__status--cancelled { background: var(--danger-bg); color: var(--danger-text); }
.appointment-card__meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; margin-bottom: 12px; }
.meta-item { background: #f9fbfb; border-radius: 14px; padding: 10px 12px; border: 1px solid #f0f5f5; }
.meta-item__label { font-size: 11px; color: var(--text-muted); margin-bottom: 5px; }
.meta-item__value { font-size: 13px; color: var(--text-primary); font-weight: 600; line-height: 1.5; }
.appointment-card__actions { display: flex; gap: 10px; }
.appointment-card__btn {
flex: 1; height: 40px; border-radius: 20px; font-size: 14px; font-weight: 600; border: none; cursor: pointer;
}
.appointment-card__btn--ghost { background: #f5f7fa; color: var(--text-secondary); border: 1px solid #ebeff2; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">我的预约</span>
</div>
<div class="scroll-content">
<div class="tabs">
<a class="tabs__item" href="my-appointments-all.html">全部</a>
<a class="tabs__item" href="my-appointments.html">待体检</a>
<a class="tabs__item" href="my-appointments-done.html">已完成</a>
<a class="tabs__item tabs__item--active" href="my-appointments-cancelled.html">已取消</a>
</div>
<div class="appointment-list">
<a class="appointment-card" href="my-appointments-detail-cancelled.html" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">胃肠镜体检</div>
<div class="appointment-card__status appointment-card__status--cancelled">已取消</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-10</div></div>
</div>
</a>
<a class="appointment-card" href="my-appointments-detail-cancelled.html" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--cancelled">已取消</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-01-15</div></div>
</div>
</a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,439 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约详情</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-light: #f6ffed;
--success-text: #52c41a;
--danger: #ff4d4f;
--danger-light: #fff1f0;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 120px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 预约状态栏 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
margin-bottom: 14px;
}
.status-bar__left {
display: flex;
align-items: center;
gap: 8px;
}
.status-bar__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #fa8c16;
}
.status-bar__text {
font-size: 16px;
font-weight: 700;
color: #fa8c16;
}
.status-card {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 24px;
padding: 22px 18px;
color: #fff;
box-shadow: 0 12px 28px rgba(19, 194, 194, 0.24);
}
.status-card__badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255,255,255,0.18);
font-size: 12px;
font-weight: 600;
margin-bottom: 14px;
}
.status-card__title {
font-size: 24px;
font-weight: 700;
line-height: 1.25;
margin-bottom: 8px;
}
.status-card__desc {
font-size: 13px;
line-height: 1.7;
opacity: 0.92;
}
.summary-card,
.info-card {
background: #fff;
border-radius: 20px;
padding: 18px 16px;
margin-top: 14px;
margin-top: 14px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
}
.summary-list,
.info-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.summary-item,
.info-item {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: flex-start;
}
.summary-item__label,
.info-item__label {
font-size: 13px;
color: var(--text-muted);
flex-shrink: 0;
}
.summary-item__value,
.info-item__value {
font-size: 14px;
color: var(--text-primary);
font-weight: 600;
text-align: right;
line-height: 1.6;
}
.summary-item--fee {
margin-top: 2px;
padding: 14px;
border-radius: 18px;
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3;
align-items: center;
}
.summary-item--fee .summary-item__label {
color: var(--primary-dark);
font-weight: 600;
}
.summary-item--fee .summary-item__value {
color: var(--primary-dark);
font-size: 24px;
font-weight: 800;
}
.person-card {
margin-top: 14px;
background: linear-gradient(135deg, #ffffff 0%, #f8fffe 100%);
border-radius: 20px;
padding: 16px;
border: 1px solid #e4f7f5;
box-shadow: var(--shadow);
}
.person-card__header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.person-card__avatar {
width: 48px;
height: 48px;
border-radius: 16px;
background: linear-gradient(135deg, #13c2c2 0%, #5cdbd3 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
flex-shrink: 0;
}
.person-card__name {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 4px;
}
.person-card__meta {
font-size: 12px;
color: var(--text-muted);
}
.tips-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 16px;
border: 1px solid #eef5f5;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
}
.tips-card__title {
font-size: 15px;
font-weight: 700;
margin-bottom: 10px;
}
.tips-card__item {
display: flex;
gap: 8px;
font-size: 13px;
line-height: 1.7;
color: var(--text-secondary);
margin-bottom: 8px;
}
.tips-card__item:last-child { margin-bottom: 0; }
.tips-card__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--primary);
margin-top: 8px;
flex-shrink: 0;
}
/* 已选体检项目 */
.project-section {
background: #fff; border-radius: 20px; padding: 18px 16px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06); margin-top: 14px;
}
.project-section__header {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px;
}
.project-section__count { font-size: 13px; color: var(--text-muted); }
.project-section__count em { font-style: normal; color: var(--primary-dark); font-weight: 700; }
.project-tabs {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
padding: 5px; background: #f5f7f7; border-radius: 14px; margin-bottom: 14px;
}
.project-tab {
height: 34px; border: none; border-radius: 10px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
}
.project-tab--active {
background: #fff; color: var(--primary-dark);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.project-list-panel {
max-height: 280px; overflow-y: auto; padding-right: 2px;
}
.project-list-panel::-webkit-scrollbar { display: none; }
.project-list { display: flex; flex-direction: column; gap: 10px; }
.project-item {
display: flex; align-items: center; gap: 12px; padding: 12px 14px;
background: #f9fbfb; border-radius: 14px; border: 1px solid #eef5f5;
}
.project-item__dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--primary); flex-shrink: 0;
}
.project-item__info { flex: 1; }
.project-item__name { font-size: 14px; font-weight: 600; margin-bottom: 2px; }
.project-item__desc { font-size: 12px; color: var(--text-muted); }
.project-item__price { font-size: 13px; font-weight: 700; color: var(--primary-dark); flex-shrink: 0; }
.project-item__badge {
display: inline-flex; align-items: center; height: 20px; padding: 0 8px;
border-radius: 10px; font-size: 10px; font-weight: 600; flex-shrink: 0;
}
.project-item__badge--normal { background: #f5f5f5; color: var(--text-muted); }
.project-item__badge--key { background: #fff1f0; color: #ff4d4f; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回至: my-appointments-cancelled.html -->
<a class="nav-back" href="my-appointments-cancelled.html"></a>
<span class="nav-title">体检预约详情</span>
</div>
<div class="scroll-content">
<!-- 预约状态栏 -->
<div class="status-bar">
<div class="status-bar__left">
<div class="status-bar__dot" style="background:#ff4d4f;"></div>
<div class="status-bar__text" style="color:#ff4d4f;">已取消</div>
</div>
</div>
<div class="summary-card">
<div class="section-title">常规体检</div>
<div class="summary-list">
<div class="summary-item">
<div class="summary-item__label">体检医院</div>
<div class="summary-item__value">宁夏宝石花医院</div>
</div>
<div class="summary-item">
<div class="summary-item__label">预约日期</div>
<div class="summary-item__value">2026-03-23</div>
</div>
<div class="summary-item">
<div class="summary-item__label">体检员工</div>
<div class="summary-item__value">张旭</div>
</div>
<div class="summary-item">
<div class="summary-item__label">单位部门</div>
<div class="summary-item__value">采油一厂·xx作业区</div>
</div>
</div>
</div>
<!-- 已选体检项目 -->
<div class="project-section">
<div class="project-section__header">
<div class="section-title" style="margin-bottom:0">已选体检项目</div>
<span class="project-section__count"><em id="totalCount">14</em></span>
</div>
<!-- [交互] 点击切换自选项目/必检项目 -->
<div class="project-tabs">
<button class="project-tab project-tab--active" type="button" data-tab="optional">自选项目(6)</button>
<button class="project-tab" type="button" data-tab="required">必检项目(8)</button>
</div>
<div class="project-list-panel">
<div class="project-list" id="projectList"></div>
</div>
</div>
</div>
</div>
<script>
const projects = [
{ name: '一般检查', desc: '身高、体重、血压、视力', price: 120, type: 'required', level: 'normal' },
{ name: '血常规', desc: '血红蛋白、白细胞、血小板等', price: 80, type: 'required', level: 'normal' },
{ name: '尿常规', desc: '尿蛋白、尿糖、尿潜血等', price: 45, type: 'required', level: 'normal' },
{ name: '肝功能', desc: 'ALT、AST、总胆红素等', price: 160, type: 'required', level: 'key' },
{ name: '肾功能', desc: '肌酐、尿素氮、尿酸等', price: 110, type: 'required', level: 'key' },
{ name: '血脂四项', desc: '总胆固醇、甘油三酯、HDL、LDL', price: 90, type: 'required', level: 'normal' },
{ name: '空腹血糖', desc: '空腹静脉血糖检测', price: 40, type: 'required', level: 'normal' },
{ name: '心电图', desc: '十二导联心电图检查', price: 75, type: 'required', level: 'key' },
{ name: '胸部X光', desc: '胸部正位X线摄影', price: 140, type: 'optional', level: 'normal' },
{ name: '腹部彩超', desc: '肝胆脾胰肾彩色超声', price: 180, type: 'optional', level: 'key' },
{ name: '甲状腺彩超', desc: '甲状腺彩色多普勒超声', price: 150, type: 'optional', level: 'key' },
{ name: '肿瘤标志物', desc: 'AFP、CEA、CA199等', price: 280, type: 'optional', level: 'key' },
{ name: '幽门螺杆菌', desc: 'C14呼气试验', price: 120, type: 'optional', level: 'normal' },
{ name: '骨密度检测', desc: '超声骨密度检测', price: 100, type: 'optional', level: 'normal' }
];
let activeTab = 'optional';
const listEl = document.getElementById('projectList');
const tabButtons = document.querySelectorAll('.project-tab');
function renderProjects() {
const filtered = projects.filter(item => item.type === activeTab);
listEl.innerHTML = filtered.map(item => {
const badgeText = item.level === 'key' ? '重点' : '一般';
const badgeClass = item.level === 'key' ? 'project-item__badge--key' : 'project-item__badge--normal';
return `
<div class="project-item">
<div class="project-item__dot"></div>
<div class="project-item__info">
<div class="project-item__name">${item.name}</div>
<div class="project-item__desc">${item.desc}</div>
</div>
<div class="project-item__price">¥${item.price}</div>
<div class="project-item__badge ${badgeClass}">${badgeText}</div>
</div>
`;
}).join('');
}
tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
tabButtons.forEach(item => item.classList.remove('project-tab--active'));
btn.classList.add('project-tab--active');
activeTab = btn.dataset.tab;
renderProjects();
});
});
renderProjects();
</script>
</body>
</html>
@@ -0,0 +1,668 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约详情</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-light: #f6ffed;
--success-text: #52c41a;
--danger: #ff4d4f;
--danger-light: #fff1f0;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 120px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 预约状态栏 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
margin-bottom: 14px;
}
.status-bar__left {
display: flex;
align-items: center;
gap: 8px;
}
.status-bar__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #fa8c16;
}
.status-bar__text {
font-size: 16px;
font-weight: 700;
color: #fa8c16;
}
.status-bar__btn {
padding: 6px 14px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
border: 1px solid rgba(255, 77, 79, 0.3);
background: var(--danger-light);
color: var(--danger);
cursor: pointer;
}
.status-bar__btn--disabled {
border: 1px solid #d9d9d9;
background: #f5f5f5;
color: #bfbfbf;
cursor: not-allowed;
}
/* 确认弹窗 */
.confirm-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 200;
justify-content: center;
align-items: center;
}
.confirm-overlay--visible { display: flex; }
.confirm-dialog {
width: 300px;
background: #fff;
border-radius: 20px;
padding: 24px 20px 18px;
text-align: center;
}
.confirm-dialog__title {
font-size: 17px;
font-weight: 700;
margin-bottom: 14px;
color: var(--text-primary);
}
.confirm-dialog__info {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 20px;
padding: 12px;
background: #f9fbfb;
border-radius: 12px;
border: 1px solid #f0f5f5;
}
.confirm-dialog__row {
display: flex;
justify-content: space-between;
font-size: 13px;
}
.confirm-dialog__label { color: var(--text-muted); }
.confirm-dialog__value { color: var(--text-primary); font-weight: 600; }
.confirm-dialog__actions {
display: flex;
gap: 10px;
}
.confirm-dialog__btn {
flex: 1;
height: 42px;
border-radius: 21px;
font-size: 14px;
font-weight: 600;
border: none;
cursor: pointer;
}
.confirm-dialog__btn--cancel {
background: #f5f7fa;
color: var(--text-secondary);
border: 1px solid #ebeff2;
}
.confirm-dialog__btn--confirm {
background: var(--danger);
color: #fff;
}
.toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.76);
color: #fff;
padding: 14px 24px;
border-radius: 12px;
font-size: 14px;
z-index: 200;
text-align: center;
max-width: 280px;
line-height: 1.6;
display: none;
}
.toast--visible { display: block; }
.status-card {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 24px;
padding: 22px 18px;
color: #fff;
box-shadow: 0 12px 28px rgba(19, 194, 194, 0.24);
}
.status-card__badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255,255,255,0.18);
font-size: 12px;
font-weight: 600;
margin-bottom: 14px;
}
.status-card__title {
font-size: 24px;
font-weight: 700;
line-height: 1.25;
margin-bottom: 8px;
}
.status-card__desc {
font-size: 13px;
line-height: 1.7;
opacity: 0.92;
}
.summary-card,
.info-card {
background: #fff;
border-radius: 20px;
padding: 18px 16px;
margin-top: 14px;
margin-top: 14px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
}
.summary-list,
.info-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.summary-item,
.info-item {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: flex-start;
}
.summary-item__label,
.info-item__label {
font-size: 13px;
color: var(--text-muted);
flex-shrink: 0;
}
.summary-item__value,
.info-item__value {
font-size: 14px;
color: var(--text-primary);
font-weight: 600;
text-align: right;
line-height: 1.6;
}
.summary-item--fee {
margin-top: 2px;
padding: 14px;
border-radius: 18px;
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3;
align-items: center;
}
.summary-item--fee .summary-item__label {
color: var(--primary-dark);
font-weight: 600;
}
.summary-item--fee .summary-item__value {
color: var(--primary-dark);
font-size: 24px;
font-weight: 800;
}
.person-card {
margin-top: 14px;
background: linear-gradient(135deg, #ffffff 0%, #f8fffe 100%);
border-radius: 20px;
padding: 16px;
border: 1px solid #e4f7f5;
box-shadow: var(--shadow);
}
.person-card__header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.person-card__avatar {
width: 48px;
height: 48px;
border-radius: 16px;
background: linear-gradient(135deg, #13c2c2 0%, #5cdbd3 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
flex-shrink: 0;
}
.person-card__name {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 4px;
}
.person-card__meta {
font-size: 12px;
color: var(--text-muted);
}
.tips-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 16px;
border: 1px solid #eef5f5;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
}
.tips-card__title {
font-size: 15px;
font-weight: 700;
margin-bottom: 10px;
}
.tips-card__item {
display: flex;
gap: 8px;
font-size: 13px;
line-height: 1.7;
color: var(--text-secondary);
margin-bottom: 8px;
}
.tips-card__item:last-child { margin-bottom: 0; }
.tips-card__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--primary);
margin-top: 8px;
flex-shrink: 0;
}
.qrcode-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.qrcode-wrap__img {
width: 160px;
height: 160px;
border-radius: 12px;
border: 1px solid #eef5f5;
cursor: pointer;
transition: transform 0.2s;
}
.qrcode-wrap__img:hover { transform: scale(1.04); }
.qrcode-wrap__hint {
font-size: 12px;
color: var(--text-muted);
}
/* 二维码放大遮罩 */
.qr-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.6);
z-index: 100;
justify-content: center;
align-items: center;
cursor: pointer;
}
.qr-overlay--visible { display: flex; }
.qr-overlay__img {
width: 260px;
height: 260px;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
/* 体检进度 */
.progress-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 18px 16px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.progress-card__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
.progress-card__title {
font-size: 16px;
font-weight: 700;
}
.progress-card__ratio {
font-size: 13px;
color: var(--primary-dark);
font-weight: 600;
}
.progress-bar-lg {
height: 10px;
border-radius: 999px;
background: #eef3f3;
overflow: hidden;
margin-bottom: 16px;
}
.progress-bar-lg__fill {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, #13c2c2 0%, #5cdbd3 100%);
}
.project-tabs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 14px;
}
.project-tabs__btn {
height: 34px;
border: none;
border-radius: 12px;
background: #f5f7fa;
color: var(--text-secondary);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.project-tabs__btn--active {
background: var(--primary-light);
color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.project-list { display: flex; flex-direction: column; gap: 8px; }
.project-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 14px;
background: #f9fbfb;
border: 1px solid #f0f5f5;
}
.project-item__icon {
width: 28px;
height: 28px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 14px;
}
.project-item__icon--done {
background: var(--success-light);
color: var(--success-text);
}
.project-item__icon--pending {
background: #fff7e6;
color: #fa8c16;
}
.project-item__name {
flex: 1;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.project-item__status {
font-size: 12px;
font-weight: 600;
}
.project-item__status--done { color: var(--success-text); }
.project-item__status--pending { color: #fa8c16; }
.hidden { display: none !important; }
.bottom-actions {
position: absolute;
left: 16px;
right: 16px;
bottom: 18px;
display: flex;
gap: 12px;
padding-bottom: 16px;
}
.bottom-actions__btn {
flex: 1;
height: 48px;
border-radius: 24px;
font-size: 15px;
font-weight: 600;
border: none;
cursor: pointer;
}
.bottom-actions__btn--ghost {
background: var(--danger-light);
color: var(--danger);
border: 1px solid rgba(255, 77, 79, 0.2);
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回至: my-appointments-done.html -->
<a class="nav-back" href="my-appointments-done.html"></a>
<span class="nav-title">体检预约详情</span>
</div>
<div class="scroll-content">
<!-- 预约状态栏 -->
<div class="status-bar">
<div class="status-bar__left">
<div class="status-bar__dot" style="background:#52c41a;"></div>
<div class="status-bar__text" style="color:#52c41a;">已完成</div>
</div>
</div>
<div class="summary-card">
<div class="section-title">常规体检</div>
<div class="summary-list">
<div class="summary-item">
<div class="summary-item__label">体检医院</div>
<div class="summary-item__value">宁夏宝石花医院</div>
</div>
<div class="summary-item">
<div class="summary-item__label">预约日期</div>
<div class="summary-item__value">2026-03-23</div>
</div>
<div class="summary-item">
<div class="summary-item__label">体检员工</div>
<div class="summary-item__value">张旭</div>
</div>
<div class="summary-item">
<div class="summary-item__label">单位部门</div>
<div class="summary-item__value">采油一厂·xx作业区</div>
</div>
</div>
</div>
<!-- 体检进度 -->
<div class="progress-card">
<div class="progress-card__header">
<div class="progress-card__title">体检进度</div>
<div class="progress-card__ratio" id="progressRatio">26 / 26 项</div>
</div>
<div class="progress-bar-lg"><div class="progress-bar-lg__fill" id="progressFill" style="width: 100%;"></div></div>
<!-- [交互] 点击切换已检/未检 Tab -->
<div class="project-tabs" id="projectTabs">
<button class="project-tabs__btn project-tabs__btn--active" type="button" id="tabDone" onclick="switchTab(0)">已检项目 (0)</button>
<button class="project-tabs__btn" type="button" id="tabPending" onclick="switchTab(1)">未检项目 (0)</button>
</div>
<!-- 已检项目列表(JS动态生成) -->
<div class="project-list" id="doneList"></div>
<!-- 未检项目列表(默认隐藏,JS动态生成) -->
<div class="project-list hidden" id="pendingList"></div>
</div>
</div>
</div>
<!-- <div class="bottom-actions">-->
<!-- &lt;!&ndash; [交互] 点击跳转至: my-appointments.html &ndash;&gt;-->
<!-- <button class="bottom-actions__btn bottom-actions__btn&#45;&#45;ghost" onclick="window.location.href='my-appointments.html'">取消预约</button>-->
<!-- &lt;!&ndash; [交互] 点击跳转至: hospital-checkin.html &ndash;&gt;-->
<!-- <button class="bottom-actions__btn bottom-actions__btn&#45;&#45;primary" onclick="window.location.href='hospital-checkin.html'">到院体检</button>-->
<!-- </div>-->
<!-- </div>-->
<!-- 二维码放大遮罩 -->
<div class="qr-overlay" id="qrOverlay" onclick="this.classList.remove('qr-overlay--visible')">
<svg class="qr-overlay__img" viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
<rect width="160" height="160" fill="#fff"/>
<rect x="12" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="108" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="114" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="120" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="12" y="108" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="114" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="120" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="60" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="12" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="24" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="24" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="36" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="36" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="28" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="60" width="8" height="8" fill="#1a1a1a"/>
<rect x="20" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="72" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="36" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="84" width="8" height="8" fill="#1a1a1a"/>
<rect x="28" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="52" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="96" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="108" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="120" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="132" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="140" width="8" height="8" fill="#1a1a1a"/>
</svg>
</div>
<script>
function switchTab(idx) {
var btns = document.querySelectorAll('.project-tabs__btn');
btns.forEach(function(b) { b.classList.remove('project-tabs__btn--active'); });
btns[idx].classList.add('project-tabs__btn--active');
document.getElementById('doneList').classList.toggle('hidden', idx !== 0);
document.getElementById('pendingList').classList.toggle('hidden', idx !== 1);
}
var totalCount = 26;
var doneCount = 26;
var allProjects = [
'一般检查', '内科', '外科', '眼科检查', '耳鼻喉科', '口腔科',
'血常规', '尿常规', '肝功能', '肾功能', '血脂四项', '空腹血糖',
'甲状腺功能', '肿瘤标志物', '心电图', '胸部DR', '腹部彩超',
'甲状腺彩超', '泌尿系彩超', '心脏彩超', '颈动脉彩超',
'骨密度检测', '肺功能', '幽门螺杆菌', '中医体质辨识', '总检报告'
];
function renderItem(name) {
return '<div class="project-item">' +
'<div class="project-item__icon project-item__icon--done">&#10003;</div>' +
'<div class="project-item__name">' + name + '</div>' +
'<div class="project-item__status project-item__status--done">已完成</div></div>';
}
document.getElementById('doneList').innerHTML = allProjects.map(function(n) { return renderItem(n); }).join('');
document.getElementById('pendingList').innerHTML = '<div class="project-item"><div class="project-item__icon project-item__icon--done">&#10003;</div><div class="project-item__name">当前预约项目已全部完成</div><div class="project-item__status project-item__status--done">完成</div></div>';
document.getElementById('tabDone').textContent = '已检项目 (' + doneCount + ')';
document.getElementById('tabPending').textContent = '未检项目 (0)';
document.getElementById('progressRatio').textContent = doneCount + ' / ' + totalCount + ' 项';
document.getElementById('progressFill').style.width = '100%';
</script>
</body>
</html>
@@ -0,0 +1,771 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检预约详情</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-light: #f6ffed;
--success-text: #52c41a;
--danger: #ff4d4f;
--danger-light: #fff1f0;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--border: #e8f3f3;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%);
padding-bottom: 120px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 预约状态栏 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
margin-bottom: 14px;
}
.status-bar__left {
display: flex;
align-items: center;
gap: 8px;
}
.status-bar__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #fa8c16;
}
.status-bar__text {
font-size: 16px;
font-weight: 700;
color: #fa8c16;
}
.status-bar__btn {
padding: 6px 14px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
border: 1px solid rgba(255, 77, 79, 0.3);
background: var(--danger-light);
color: var(--danger);
cursor: pointer;
}
.status-bar__btn--disabled {
border: 1px solid #d9d9d9;
background: #f5f5f5;
color: #bfbfbf;
cursor: not-allowed;
}
/* 确认弹窗 */
.confirm-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 200;
justify-content: center;
align-items: center;
}
.confirm-overlay--visible { display: flex; }
.confirm-dialog {
width: 300px;
background: #fff;
border-radius: 20px;
padding: 24px 20px 18px;
text-align: center;
}
.confirm-dialog__title {
font-size: 17px;
font-weight: 700;
margin-bottom: 14px;
color: var(--text-primary);
}
.confirm-dialog__info {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 20px;
padding: 12px;
background: #f9fbfb;
border-radius: 12px;
border: 1px solid #f0f5f5;
}
.confirm-dialog__row {
display: flex;
justify-content: space-between;
font-size: 13px;
}
.confirm-dialog__label { color: var(--text-muted); }
.confirm-dialog__value { color: var(--text-primary); font-weight: 600; }
.confirm-dialog__actions {
display: flex;
gap: 10px;
}
.confirm-dialog__btn {
flex: 1;
height: 42px;
border-radius: 21px;
font-size: 14px;
font-weight: 600;
border: none;
cursor: pointer;
}
.confirm-dialog__btn--cancel {
background: #f5f7fa;
color: var(--text-secondary);
border: 1px solid #ebeff2;
}
.confirm-dialog__btn--confirm {
background: var(--danger);
color: #fff;
}
.toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.76);
color: #fff;
padding: 14px 24px;
border-radius: 12px;
font-size: 14px;
z-index: 200;
text-align: center;
max-width: 280px;
line-height: 1.6;
display: none;
}
.toast--visible { display: block; }
.status-card {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
border-radius: 24px;
padding: 22px 18px;
color: #fff;
box-shadow: 0 12px 28px rgba(19, 194, 194, 0.24);
}
.status-card__badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255,255,255,0.18);
font-size: 12px;
font-weight: 600;
margin-bottom: 14px;
}
.status-card__title {
font-size: 24px;
font-weight: 700;
line-height: 1.25;
margin-bottom: 8px;
}
.status-card__desc {
font-size: 13px;
line-height: 1.7;
opacity: 0.92;
}
.summary-card,
.info-card {
background: #fff;
border-radius: 20px;
padding: 18px 16px;
margin-top: 14px;
margin-top: 14px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
}
.summary-list,
.info-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.summary-item,
.info-item {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: flex-start;
}
.summary-item__label,
.info-item__label {
font-size: 13px;
color: var(--text-muted);
flex-shrink: 0;
}
.summary-item__value,
.info-item__value {
font-size: 14px;
color: var(--text-primary);
font-weight: 600;
text-align: right;
line-height: 1.6;
}
.summary-item--fee {
margin-top: 2px;
padding: 14px;
border-radius: 18px;
background: linear-gradient(135deg, #f0fffe 0%, #f7fffd 100%);
border: 1px solid #d7f5f3;
align-items: center;
}
.summary-item--fee .summary-item__label {
color: var(--primary-dark);
font-weight: 600;
}
.summary-item--fee .summary-item__value {
color: var(--primary-dark);
font-size: 24px;
font-weight: 800;
}
.person-card {
margin-top: 14px;
background: linear-gradient(135deg, #ffffff 0%, #f8fffe 100%);
border-radius: 20px;
padding: 16px;
border: 1px solid #e4f7f5;
box-shadow: var(--shadow);
}
.person-card__header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.person-card__avatar {
width: 48px;
height: 48px;
border-radius: 16px;
background: linear-gradient(135deg, #13c2c2 0%, #5cdbd3 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
flex-shrink: 0;
}
.person-card__name {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 4px;
}
.person-card__meta {
font-size: 12px;
color: var(--text-muted);
}
.tips-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 16px;
border: 1px solid #eef5f5;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
}
.tips-card__title {
font-size: 15px;
font-weight: 700;
margin-bottom: 10px;
}
.tips-card__item {
display: flex;
gap: 8px;
font-size: 13px;
line-height: 1.7;
color: var(--text-secondary);
margin-bottom: 8px;
}
.tips-card__item:last-child { margin-bottom: 0; }
.tips-card__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--primary);
margin-top: 8px;
flex-shrink: 0;
}
.qrcode-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.qrcode-wrap__img {
width: 160px;
height: 160px;
border-radius: 12px;
border: 1px solid #eef5f5;
cursor: pointer;
transition: transform 0.2s;
}
.qrcode-wrap__img:hover { transform: scale(1.04); }
.qrcode-wrap__hint {
font-size: 12px;
color: var(--text-muted);
}
/* 二维码放大遮罩 */
.qr-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.6);
z-index: 100;
justify-content: center;
align-items: center;
cursor: pointer;
}
.qr-overlay--visible { display: flex; }
.qr-overlay__img {
width: 260px;
height: 260px;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
/* 体检进度 */
.progress-card {
margin-top: 14px;
background: #fff;
border-radius: 20px;
padding: 18px 16px;
box-shadow: var(--shadow);
border: 1px solid rgba(19, 194, 194, 0.06);
}
.progress-card__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
.progress-card__title {
font-size: 16px;
font-weight: 700;
}
.progress-card__ratio {
font-size: 13px;
color: var(--primary-dark);
font-weight: 600;
}
.progress-bar-lg {
height: 10px;
border-radius: 999px;
background: #eef3f3;
overflow: hidden;
margin-bottom: 16px;
}
.progress-bar-lg__fill {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, #13c2c2 0%, #5cdbd3 100%);
}
.project-tabs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 14px;
}
.project-tabs__btn {
height: 34px;
border: none;
border-radius: 12px;
background: #f5f7fa;
color: var(--text-secondary);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.project-tabs__btn--active {
background: var(--primary-light);
color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.project-list { display: flex; flex-direction: column; gap: 8px; }
.project-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 14px;
background: #f9fbfb;
border: 1px solid #f0f5f5;
}
.project-item__icon {
width: 28px;
height: 28px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 14px;
}
.project-item__icon--done {
background: var(--success-light);
color: var(--success-text);
}
.project-item__icon--pending {
background: #fff7e6;
color: #fa8c16;
}
.project-item__name {
flex: 1;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.project-item__status {
font-size: 12px;
font-weight: 600;
}
.project-item__status--done { color: var(--success-text); }
.project-item__status--pending { color: #fa8c16; }
.hidden { display: none !important; }
.bottom-actions {
position: absolute;
left: 16px;
right: 16px;
bottom: 18px;
display: flex;
gap: 12px;
padding-bottom: 16px;
}
.bottom-actions__btn {
flex: 1;
height: 48px;
border-radius: 24px;
font-size: 15px;
font-weight: 600;
border: none;
cursor: pointer;
}
.bottom-actions__btn--ghost {
background: var(--danger-light);
color: var(--danger);
border: 1px solid rgba(255, 77, 79, 0.2);
}
.bottom-actions__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 10px 20px rgba(19, 194, 194, 0.22);
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回至: my-appointments.html -->
<a class="nav-back" href="my-appointments.html"></a>
<span class="nav-title">体检预约详情</span>
</div>
<div class="scroll-content">
<!-- 预约状态栏 -->
<div class="status-bar">
<div class="status-bar__left">
<div class="status-bar__dot"></div>
<div class="status-bar__text">待体检</div>
</div>
<button class="status-bar__btn status-bar__btn--disabled" id="cancelBtn" disabled>取消预约</button>
</div>
<div class="summary-card">
<div class="section-title">常规体检</div>
<div class="summary-list">
<div class="summary-item">
<div class="summary-item__label">体检医院</div>
<div class="summary-item__value">宁夏宝石花医院</div>
</div>
<div class="summary-item">
<div class="summary-item__label">预约日期</div>
<div class="summary-item__value">2026-03-23</div>
</div>
<div class="summary-item">
<div class="summary-item__label">体检员工</div>
<div class="summary-item__value">张旭</div>
</div>
<div class="summary-item">
<div class="summary-item__label">单位部门</div>
<div class="summary-item__value">采油一厂·xx作业区</div>
</div>
</div>
</div>
<div class="person-card">
<div class="section-title">体检签到码</div>
<div class="qrcode-wrap">
<!-- [交互] 点击二维码放大展示 -->
<svg class="qrcode-wrap__img" onclick="document.getElementById('qrOverlay').classList.add('qr-overlay--visible')" viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
<rect width="160" height="160" fill="#fff"/>
<!-- 左上定位块 -->
<rect x="12" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/>
<rect x="18" y="18" width="28" height="28" rx="2" fill="#fff"/>
<rect x="24" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<!-- 右上定位块 -->
<rect x="108" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/>
<rect x="114" y="18" width="28" height="28" rx="2" fill="#fff"/>
<rect x="120" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<!-- 左下定位块 -->
<rect x="12" y="108" width="40" height="40" rx="4" fill="#1a1a1a"/>
<rect x="18" y="114" width="28" height="28" rx="2" fill="#fff"/>
<rect x="24" y="120" width="16" height="16" rx="2" fill="#1a1a1a"/>
<!-- 数据区域模拟 -->
<rect x="60" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="12" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="24" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="24" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="36" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="36" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="28" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="60" width="8" height="8" fill="#1a1a1a"/>
<rect x="20" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="72" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="36" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="84" width="8" height="8" fill="#1a1a1a"/>
<rect x="28" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="52" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="96" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="108" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="120" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="132" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="140" width="8" height="8" fill="#1a1a1a"/>
</svg>
<span class="qrcode-wrap__hint">点击二维码可放大查看</span>
</div>
</div>
<!-- 体检进度 -->
<div class="progress-card">
<div class="progress-card__header">
<div class="progress-card__title">体检进度</div>
<div class="progress-card__ratio" id="progressRatio">5 / 26 项</div>
</div>
<div class="progress-bar-lg"><div class="progress-bar-lg__fill" id="progressFill" style="width: 19.2%;"></div></div>
<!-- [交互] 点击切换已检/未检 Tab -->
<div class="project-tabs" id="projectTabs">
<button class="project-tabs__btn project-tabs__btn--active" type="button" id="tabDone" onclick="switchTab(0)">已检项目 (0)</button>
<button class="project-tabs__btn" type="button" id="tabPending" onclick="switchTab(1)">未检项目 (0)</button>
</div>
<!-- 已检项目列表(JS动态生成) -->
<div class="project-list" id="doneList"></div>
<!-- 未检项目列表(默认隐藏,JS动态生成) -->
<div class="project-list hidden" id="pendingList"></div>
</div>
</div>
</div>
<!-- <div class="bottom-actions">-->
<!-- &lt;!&ndash; [交互] 点击跳转至: my-appointments.html &ndash;&gt;-->
<!-- <button class="bottom-actions__btn bottom-actions__btn&#45;&#45;ghost" onclick="window.location.href='my-appointments.html'">取消预约</button>-->
<!-- &lt;!&ndash; [交互] 点击跳转至: hospital-checkin.html &ndash;&gt;-->
<!-- <button class="bottom-actions__btn bottom-actions__btn&#45;&#45;primary" onclick="window.location.href='hospital-checkin.html'">到院体检</button>-->
<!-- </div>-->
<!-- </div>-->
<!-- 二维码放大遮罩 -->
<div class="qr-overlay" id="qrOverlay" onclick="this.classList.remove('qr-overlay--visible')">
<svg class="qr-overlay__img" viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
<rect width="160" height="160" fill="#fff"/>
<rect x="12" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="108" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="114" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="120" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="12" y="108" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="114" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="120" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="60" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="12" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="24" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="24" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="36" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="36" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="28" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="60" width="8" height="8" fill="#1a1a1a"/>
<rect x="20" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="72" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="36" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="84" width="8" height="8" fill="#1a1a1a"/>
<rect x="28" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="52" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="96" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="108" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="120" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="132" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="140" width="8" height="8" fill="#1a1a1a"/>
</svg>
</div>
<script>
function switchTab(idx) {
var btns = document.querySelectorAll('.project-tabs__btn');
btns.forEach(function(b) { b.classList.remove('project-tabs__btn--active'); });
btns[idx].classList.add('project-tabs__btn--active');
document.getElementById('doneList').classList.toggle('hidden', idx !== 0);
document.getElementById('pendingList').classList.toggle('hidden', idx !== 1);
}
function showToast(msg) {
var t = document.getElementById('cancelToast');
t.textContent = msg;
t.classList.add('toast--visible');
setTimeout(function() { t.classList.remove('toast--visible'); }, 2000);
}
// 读取 URL 参数
var params = new URLSearchParams(window.location.search);
var doneCount = parseInt(params.get('done') || '5', 10);
var totalCount = parseInt(params.get('total') || '26', 10);
var hospital = params.get('hospital') || '宁夏宝石花医院';
var date = params.get('date') || '2026-03-23';
// 动态更新体检进度
var pct = totalCount > 0 ? Math.round(doneCount / totalCount * 1000) / 10 : 0;
document.getElementById('progressRatio').textContent = doneCount + ' / ' + totalCount + ' 项';
document.getElementById('progressFill').style.width = pct + '%';
// 全部体检项目池(按常规体检顺序)
var allProjects = [
'一般检查', '内科', '外科', '眼科检查', '耳鼻喉科', '口腔科',
'血常规', '尿常规', '肝功能', '肾功能', '血脂四项', '空腹血糖',
'甲状腺功能', '肿瘤标志物', '心电图', '胸部DR', '腹部彩超',
'甲状腺彩超', '泌尿系彩超', '心脏彩超', '颈动脉彩超',
'骨密度检测', '肺功能', '幽门螺杆菌', '中医体质辨识', '总检报告'
];
// 取前 totalCount 个项目,前 doneCount 个为已检
var projects = allProjects.slice(0, totalCount);
var doneProjects = projects.slice(0, doneCount);
var pendingProjects = projects.slice(doneCount);
function renderItem(name, isDone) {
return '<div class="project-item">' +
'<div class="project-item__icon project-item__icon--' + (isDone ? 'done' : 'pending') + '">' +
(isDone ? '&#10003;' : '&#9679;') + '</div>' +
'<div class="project-item__name">' + name + '</div>' +
'<div class="project-item__status project-item__status--' + (isDone ? 'done' : 'pending') + '">' +
(isDone ? '已完成' : '未检') + '</div></div>';
}
document.getElementById('doneList').innerHTML = doneProjects.map(function(n) { return renderItem(n, true); }).join('');
document.getElementById('pendingList').innerHTML = pendingProjects.map(function(n) { return renderItem(n, false); }).join('');
document.getElementById('tabDone').textContent = '已检项目 (' + doneCount + ')';
document.getElementById('tabPending').textContent = '未检项目 (' + pendingProjects.length + ')';
// done=0 时默认显示未检 Tab
if (doneCount === 0) {
switchTab(1);
}
// 根据进度控制取消按钮状态
var cancelBtn = document.getElementById('cancelBtn');
if (doneCount > 0) {
// 已有体检进度 → 按钮禁用,点击弹 toast
cancelBtn.classList.add('status-bar__btn--disabled');
cancelBtn.disabled = false;
cancelBtn.onclick = function() {
showToast('您当前已体检了部分项目,无法取消预约');
};
} else {
// 无体检进度 → 按钮可点击,弹二次确认
cancelBtn.classList.remove('status-bar__btn--disabled');
cancelBtn.disabled = false;
cancelBtn.onclick = function() {
document.getElementById('confirmHospital').textContent = hospital;
document.getElementById('confirmDate').textContent = date;
document.getElementById('confirmOverlay').classList.add('confirm-overlay--visible');
};
}
</script>
<div class="toast" id="cancelToast"></div>
<!-- 取消预约二次确认弹窗 -->
<div class="confirm-overlay" id="confirmOverlay">
<div class="confirm-dialog">
<div class="confirm-dialog__title">是否确认取消预约</div>
<div class="confirm-dialog__info">
<div class="confirm-dialog__row">
<span class="confirm-dialog__label">体检医院</span>
<span class="confirm-dialog__value" id="confirmHospital">宁夏宝石花医院</span>
</div>
<div class="confirm-dialog__row">
<span class="confirm-dialog__label">预约日期</span>
<span class="confirm-dialog__value" id="confirmDate">2026-03-23</span>
</div>
</div>
<div class="confirm-dialog__actions">
<button class="confirm-dialog__btn confirm-dialog__btn--cancel" onclick="document.getElementById('confirmOverlay').classList.remove('confirm-overlay--visible')">再想想</button>
<button class="confirm-dialog__btn confirm-dialog__btn--confirm" onclick="window.location.href='my-appointments.html'">确认取消</button>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-bg: #f6ffed;
--success-text: #52c41a;
--warning-bg: #fff7e6;
--warning-text: #fa8c16;
--danger-bg: #fff1f0;
--danger-text: #ff4d4f;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--border: #e8f3f3;
--shadow: 0 10px 24px rgba(19, 194, 194, 0.06);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: var(--text-primary); }
.scroll-content {
flex: 1; overflow-y: auto;
background: linear-gradient(180deg, #f0fbfb 0%, #f7f9fb 140px, #f7f9fb 100%);
padding: 14px 16px 20px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.tabs {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 6px;
background: rgba(255,255,255,0.92); border-radius: 18px;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.08);
position: sticky; top: 0; z-index: 5; backdrop-filter: blur(10px);
}
.tabs__item {
height: 36px; border: none; border-radius: 14px; background: transparent;
color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer;
text-decoration: none; display: flex; align-items: center; justify-content: center;
}
.tabs__item--active {
background: var(--primary-light); color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.summary-bar {
margin: 14px 0 12px; padding: 16px; border-radius: 20px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 12px 24px rgba(19, 194, 194, 0.2);
}
.summary-bar__title { font-size: 18px; font-weight: 700; margin-bottom: 6px; }
.summary-bar__desc { font-size: 13px; line-height: 1.7; opacity: 0.92; }
.appointment-list { display: flex; flex-direction: column; gap: 12px; }
.appointment-card {
background: #fff; border-radius: 20px; padding: 16px;
border: 1px solid rgba(19, 194, 194, 0.06); box-shadow: var(--shadow);
}
.appointment-card__top { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 12px; }
.appointment-card__title { font-size: 16px; font-weight: 700; color: var(--text-primary); }
.appointment-card__status { flex-shrink: 0; padding: 5px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.appointment-card__status--done { background: var(--success-bg); color: var(--success-text); }
.appointment-card__meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; margin-bottom: 12px; }
.meta-item { background: #f9fbfb; border-radius: 14px; padding: 10px 12px; border: 1px solid #f0f5f5; }
.meta-item__label { font-size: 11px; color: var(--text-muted); margin-bottom: 5px; }
.meta-item__value { font-size: 13px; color: var(--text-primary); font-weight: 600; line-height: 1.5; }
.appointment-card__progress { margin-bottom: 14px; }
.progress-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 12px; color: var(--text-secondary); }
.progress-bar { height: 8px; border-radius: 999px; background: #eef3f3; overflow: hidden; }
.progress-bar__fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #13c2c2 0%, #5cdbd3 100%); }
.appointment-card__actions { display: flex; gap: 10px; }
.appointment-card__btn {
flex: 1; height: 40px; border-radius: 20px; font-size: 14px; font-weight: 600; border: none; cursor: pointer;
}
.appointment-card__btn--ghost { background: #f5f7fa; color: var(--text-secondary); border: 1px solid #ebeff2; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<a class="nav-back" href="index.html">&#8249;</a>
<span class="nav-title">我的预约</span>
</div>
<div class="scroll-content">
<!-- [交互] 点击 Tab 跳转对应页面 -->
<div class="tabs">
<a class="tabs__item" href="my-appointments-all.html">全部</a>
<a class="tabs__item" href="my-appointments.html">待体检</a>
<a class="tabs__item tabs__item--active" href="my-appointments-done.html">已完成</a>
<a class="tabs__item" href="my-appointments-cancelled.html">已取消</a>
</div>
<div class="appointment-list">
<a class="appointment-card" href="my-appointments-detail-done.html" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--done">已完成</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-03-23</div></div>
</div>
<div class="appointment-card__progress">
<div class="progress-head"><span>完成进度</span><span>26/26</span></div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 100%;"></div></div>
</div>
</a>
<a class="appointment-card" href="my-appointments-detail-done.html" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">胃肠镜体检</div>
<div class="appointment-card__status appointment-card__status--done">已完成</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item"><div class="meta-item__label">体检医院</div><div class="meta-item__value">宁夏宝石花医院</div></div>
<div class="meta-item"><div class="meta-item__label">预约日期</div><div class="meta-item__value">2026-02-18</div></div>
</div>
<div class="appointment-card__progress">
<div class="progress-head"><span>完成进度</span><span>2/2</span></div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 100%;"></div></div>
</div>
</a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,340 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的预约</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2;
--primary-dark: #0f9b9b;
--primary-light: #e6fffb;
--success-bg: #f6ffed;
--success-text: #52c41a;
--warning-bg: #fff7e6;
--warning-text: #fa8c16;
--danger-bg: #fff1f0;
--danger-text: #ff4d4f;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #8c8c8c;
--bg-page: #f0f2f5;
--bg-content: #f7f9fb;
--border: #e8f3f3;
--shadow: 0 10px 24px rgba(19, 194, 194, 0.06);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #f0fbfb 0%, #f7f9fb 140px, #f7f9fb 100%);
padding: 14px 16px 20px;
}
.scroll-content::-webkit-scrollbar { display: none; }
.tabs {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
padding: 6px;
background: rgba(255,255,255,0.92);
border-radius: 18px;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.08);
position: sticky;
top: 0;
z-index: 5;
backdrop-filter: blur(10px);
}
.tabs__item {
height: 36px;
border: none;
border-radius: 14px;
background: transparent;
color: var(--text-secondary);
font-size: 13px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
}
.tabs__item--active {
background: var(--primary-light);
color: var(--primary-dark);
box-shadow: inset 0 0 0 1px rgba(19, 194, 194, 0.16);
}
.summary-bar {
margin: 14px 0 12px;
padding: 16px;
border-radius: 20px;
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 12px 24px rgba(19, 194, 194, 0.2);
}
.summary-bar__title {
font-size: 18px;
font-weight: 700;
margin-bottom: 6px;
}
.summary-bar__desc {
font-size: 13px;
line-height: 1.7;
opacity: 0.92;
}
.appointment-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.appointment-card {
background: #fff;
border-radius: 20px;
padding: 16px;
border: 1px solid rgba(19, 194, 194, 0.06);
box-shadow: var(--shadow);
}
.appointment-card__top {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.appointment-card__title {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
}
.appointment-card__status {
flex-shrink: 0;
padding: 5px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.appointment-card__status--pending {
background: var(--warning-bg);
color: var(--warning-text);
}
.appointment-card__status--done {
background: var(--success-bg);
color: var(--success-text);
}
.appointment-card__status--cancelled {
background: var(--danger-bg);
color: var(--danger-text);
}
.appointment-card__meta {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 12px;
margin-bottom: 12px;
}
.meta-item {
background: #f9fbfb;
border-radius: 14px;
padding: 10px 12px;
border: 1px solid #f0f5f5;
}
.meta-item__label {
font-size: 11px;
color: var(--text-muted);
margin-bottom: 5px;
}
.meta-item__value {
font-size: 13px;
color: var(--text-primary);
font-weight: 600;
line-height: 1.5;
}
.appointment-card__progress {
margin-bottom: 14px;
}
.progress-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-size: 12px;
color: var(--text-secondary);
}
.progress-bar {
height: 8px;
border-radius: 999px;
background: #eef3f3;
overflow: hidden;
}
.progress-bar__fill {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, #13c2c2 0%, #5cdbd3 100%);
}
.appointment-card__actions {
display: flex;
gap: 10px;
}
.appointment-card__btn {
flex: 1;
height: 40px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
border: none;
cursor: pointer;
}
.appointment-card__btn--ghost {
background: #f5f7fa;
color: var(--text-secondary);
border: 1px solid #ebeff2;
}
.appointment-card__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.18);
}
.appointment-card[data-status="done"],
.appointment-card[data-status="cancelled"] {
opacity: 0.94;
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 点击返回至: index.html -->
<a class="nav-back" href="index.html"></a>
<span class="nav-title">我的预约</span>
</div>
<div class="scroll-content">
<!-- [交互] 点击 Tab 跳转对应页面 -->
<div class="tabs">
<a class="tabs__item" href="my-appointments-all.html">全部</a>
<a class="tabs__item tabs__item--active" href="my-appointments.html">待体检</a>
<a class="tabs__item" href="my-appointments-done.html">已完成</a>
<a class="tabs__item" href="my-appointments-cancelled.html">已取消</a>
</div>
<div class="appointment-list">
<!-- [交互] 点击卡片跳转至详情页,URL携带进度/医院/日期参数 -->
<a class="appointment-card" data-status="pending" href="my-appointments-detail.html?done=5&total=26&hospital=宁夏宝石花医院&date=2026-03-23" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--pending">待体检</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item">
<div class="meta-item__label">体检医院</div>
<div class="meta-item__value">宁夏宝石花医院</div>
</div>
<div class="meta-item">
<div class="meta-item__label">预约日期</div>
<div class="meta-item__value">2026-03-23</div>
</div>
</div>
<div class="appointment-card__progress">
<div class="progress-head">
<span>体检进度</span>
<span>5/26</span>
</div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 19.2%;"></div></div>
</div>
</a>
<a class="appointment-card" data-status="pending" href="my-appointments-detail.html?done=0&total=26&hospital=宁夏宝石花医院&date=2026-03-23" style="text-decoration:none;color:inherit;">
<div class="appointment-card__top">
<div class="appointment-card__title">常规体检</div>
<div class="appointment-card__status appointment-card__status--pending">待体检</div>
</div>
<div class="appointment-card__meta">
<div class="meta-item">
<div class="meta-item__label">体检医院</div>
<div class="meta-item__value">宁夏宝石花医院</div>
</div>
<div class="meta-item">
<div class="meta-item__label">预约日期</div>
<div class="meta-item__value">2026-03-23</div>
</div>
</div>
<div class="appointment-card__progress">
<div class="progress-head">
<span>体检进度</span>
<span>0/26</span>
</div>
<div class="progress-bar"><div class="progress-bar__fill" style="width: 0%;"></div></div>
</div>
</a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,215 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目详情</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #13c2c2; --primary-dark: #0f9b9b; --primary-light: #e6fffb;
--text-primary: #1a1a1a; --text-secondary: #666666; --text-muted: #8c8c8c;
--bg-page: #f0f2f5; --bg-content: #f7f9fb;
--shadow: 0 10px 30px rgba(19, 194, 194, 0.08);
}
body {
background: var(--bg-page); display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px; min-height: 844px; background: var(--bg-content);
border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: var(--text-primary); cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.scroll-content {
flex: 1; overflow-y: auto; background: #f7f9fb;
}
.scroll-content::-webkit-scrollbar { display: none; }
/* 顶部项目图片 */
.detail-hero {
width: 100%; height: 220px; background: linear-gradient(135deg, #e0f7fa 0%, #b2dfdb 50%, #80cbc4 100%);
display: flex; align-items: center; justify-content: center; position: relative;
}
.detail-hero__icon {
width: 80px; height: 80px; border-radius: 24px;
background: rgba(255,255,255,0.85); display: flex; align-items: center; justify-content: center;
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
}
.detail-hero__icon svg { width: 40px; height: 40px; color: var(--primary-dark); }
/* 内容区 */
.detail-body { padding: 20px 16px 32px; }
.detail-name {
font-size: 22px; font-weight: 800; color: var(--text-primary); margin-bottom: 6px;
}
.detail-meta {
display: flex; gap: 10px; align-items: center; margin-bottom: 18px;
}
.detail-badge {
display: inline-flex; align-items: center; height: 24px; padding: 0 12px;
border-radius: 12px; font-size: 12px; font-weight: 600;
}
.detail-badge--normal { background: #f5f5f5; color: var(--text-muted); }
.detail-badge--key { background: #fff1f0; color: #ff4d4f; }
.detail-price { font-size: 18px; font-weight: 800; color: var(--primary-dark); }
/* 卡片通用 */
.detail-card {
background: #fff; border-radius: 20px; padding: 18px 16px; margin-bottom: 14px;
box-shadow: var(--shadow); border: 1px solid rgba(19,194,194,0.06);
}
.detail-card__title {
font-size: 16px; font-weight: 700; color: var(--text-primary); margin-bottom: 12px;
display: flex; align-items: center; gap: 8px;
}
.detail-card__title-dot {
width: 4px; height: 18px; border-radius: 2px; background: var(--primary);
}
.detail-card__text {
font-size: 14px; color: var(--text-secondary); line-height: 1.8;
}
/* 视频播放器占位 */
.video-section { margin-bottom: 16px; }
.video-section:last-child { margin-bottom: 0; }
.video-section__label {
font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 10px;
display: flex; align-items: center; gap: 6px;
}
.video-section__label-icon { color: var(--primary); }
.video-player {
width: 100%; height: 180px; border-radius: 16px; overflow: hidden;
background: #1a1a1a; position: relative; display: flex; align-items: center; justify-content: center;
}
.video-player__poster {
position: absolute; inset: 0;
background: linear-gradient(135deg, #2a2a2a 0%, #3a3a3a 100%);
}
.video-player__play {
width: 56px; height: 56px; border-radius: 50%; border: none; cursor: pointer;
background: rgba(255,255,255,0.2); backdrop-filter: blur(8px);
display: flex; align-items: center; justify-content: center; position: relative; z-index: 1;
transition: transform 0.2s, background 0.2s;
}
.video-player__play:hover { transform: scale(1.08); background: rgba(255,255,255,0.3); }
.video-player__play svg { color: #fff; }
.video-player__duration {
position: absolute; bottom: 10px; right: 12px; font-size: 12px; color: rgba(255,255,255,0.7);
z-index: 1;
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<!-- [交互] 返回上一页 -->
<div class="nav-bar">
<a class="nav-back" onclick="history.back()">&#8249;</a>
<span class="nav-title">项目详情</span>
</div>
<div class="scroll-content" id="scrollContent"></div>
</div>
<script>
const projects = {
general: { name: '一般检查', desc: '身高、体重、血压、视力', price: 120, level: 'normal',
intro: '一般检查是健康体检的基础项目,通过测量身高、体重、体质指数(BMI)、血压、脉搏及视力等基本生理指标,初步评估受检者的整体健康状况,为后续专项检查提供参考依据。',
videos: { before: '03:25', after: '02:18', faq: '04:12' } },
blood: { name: '血常规', desc: '血红蛋白、白细胞、血小板等', price: 80, level: 'normal',
intro: '血常规检查通过采集静脉血样本,分析红细胞、白细胞、血小板等血液成分的数量和形态,是筛查贫血、感染、血液系统疾病等的重要基础检查项目。',
videos: { before: '02:50', after: '03:05', faq: '03:40' } },
urine: { name: '尿常规', desc: '尿蛋白、尿糖、尿潜血等', price: 45, level: 'normal',
intro: '尿常规检查通过分析尿液中的蛋白质、糖分、潜血、白细胞等成分,辅助筛查泌尿系统疾病、糖尿病、肾脏疾病等,是临床常用的基础检验项目。',
videos: { before: '02:15', after: '01:58', faq: '03:10' } },
liver: { name: '肝功能', desc: 'ALT、AST、总胆红素等', price: 160, level: 'key',
intro: '肝功能检查通过检测血清中谷丙转氨酶(ALT)、谷草转氨酶(AST)、总胆红素、白蛋白等指标,评估肝脏的代谢、合成和解毒功能,是肝脏疾病筛查的核心项目。',
videos: { before: '03:45', after: '02:30', faq: '04:20' } },
kidney: { name: '肾功能', desc: '肌酐、尿素氮、尿酸等', price: 110, level: 'key',
intro: '肾功能检查通过检测血清肌酐、尿素氮、尿酸等指标,评估肾脏的滤过和排泄功能,早期发现肾脏损伤和代谢异常,对高血压、糖尿病患者尤为重要。',
videos: { before: '03:10', after: '02:45', faq: '03:55' } },
lipid: { name: '血脂四项', desc: '总胆固醇、甘油三酯、HDL、LDL', price: 90, level: 'normal',
intro: '血脂四项检查包括总胆固醇、甘油三酯、高密度脂蛋白胆固醇(HDL-C)和低密度脂蛋白胆固醇(LDL-C),是评估心脑血管疾病风险的关键指标。',
videos: { before: '02:40', after: '03:15', faq: '04:00' } },
glucose: { name: '空腹血糖', desc: '空腹静脉血糖检测', price: 40, level: 'normal',
intro: '空腹血糖检测要求受检者空腹8-12小时后采集静脉血,测定血液中葡萄糖浓度,是筛查糖尿病和糖耐量异常的首选检查方法。',
videos: { before: '02:20', after: '01:50', faq: '03:30' } },
ecg: { name: '心电图', desc: '十二导联心电图检查', price: 75, level: 'key',
intro: '十二导联心电图通过记录心脏电活动,检测心律失常、心肌缺血、心肌梗死等心脏疾病,是心血管系统筛查的基础且不可替代的检查手段。',
videos: { before: '03:00', after: '02:25', faq: '04:35' } },
xray: { name: '胸部X光', desc: '胸部正位X线摄影', price: 140, level: 'normal',
intro: '胸部X光检查通过X射线成像技术,观察肺部、心脏、纵隔及胸廓骨骼结构,筛查肺炎、肺结核、肺部肿瘤、心脏增大等胸部疾病。',
videos: { before: '02:55', after: '02:10', faq: '03:45' } },
ultrasound: { name: '腹部B超', desc: '肝胆脾胰肾超声检查', price: 180, level: 'key',
intro: '腹部B超利用超声波成像技术,对肝脏、胆囊、脾脏、胰腺和双肾进行无创检查,可发现结石、囊肿、肿瘤等器质性病变,是腹部脏器筛查的首选方法。',
videos: { before: '03:30', after: '02:00', faq: '04:10' } },
thyroid: { name: '甲状腺彩超', desc: '甲状腺结构与结节筛查', price: 160, level: 'key',
intro: '甲状腺彩超通过高频超声探头检查甲状腺的大小、形态、内部回声及血流情况,是筛查甲状腺结节、甲状腺肿大、甲状腺炎等疾病的重要影像学手段。',
videos: { before: '02:35', after: '01:45', faq: '03:20' } },
tumor: { name: '肿瘤标志物', desc: '基础肿瘤风险筛查', price: 220, level: 'key',
intro: '肿瘤标志物检查通过检测血清中特定蛋白质或代谢产物的浓度,辅助筛查常见恶性肿瘤风险。本项目包含AFP、CEA、CA199等基础肿瘤标志物组合。',
videos: { before: '04:00', after: '03:20', faq: '05:15' } }
};
const params = new URLSearchParams(window.location.search);
const projectId = params.get('id') || 'general';
const p = projects[projectId] || projects.general;
const playIcon = `<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`;
const videoIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>`;
function renderVideo(label, duration) {
return `<div class="video-section">
<div class="video-section__label"><span class="video-section__label-icon">${videoIcon}</span>${label}</div>
<div class="video-player">
<div class="video-player__poster"></div>
<button class="video-player__play">${playIcon}</button>
<span class="video-player__duration">${duration}</span>
</div>
</div>`;
}
document.getElementById('scrollContent').innerHTML = `
<div class="detail-hero">
<div class="detail-hero__icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
</svg>
</div>
</div>
<div class="detail-body">
<div class="detail-name">${p.name}</div>
<div class="detail-meta">
<span class="detail-badge detail-badge--${p.level === 'key' ? 'key' : 'normal'}">${p.level === 'key' ? '重点项目' : '一般项目'}</span>
<span class="detail-price">¥${p.price.toFixed(2)}</span>
</div>
<div class="detail-card">
<div class="detail-card__title"><span class="detail-card__title-dot"></span>项目介绍</div>
<div class="detail-card__text">${p.intro}</div>
</div>
<div class="detail-card">
<div class="detail-card__title"><span class="detail-card__title-dot"></span>体检说明</div>
${renderVideo('检前说明', p.videos.before)}
${renderVideo('检后说明', p.videos.after)}
${renderVideo('问题说明', p.videos.faq)}
</div>
</div>
`;
</script>
</body>
</html>
@@ -0,0 +1,742 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检报告查询</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #20c7c7;
--primary-dark: #119b9b;
--primary-light: #e8fbfb;
--border: #cfeeee;
--line: #dff3f3;
--bg-page: #f0f2f5;
--bg-content: #f6fbfb;
--text-primary: #1a1a1a;
--text-secondary: #666;
--text-muted: #8c8c8c;
--danger: #ff4d4f;
--danger-light: #fff1f0;
--warning: #fa8c16;
--success: #52c41a;
--paper-shadow: 0 8px 24px rgba(17, 155, 155, 0.08);
}
body {
background: var(--bg-page);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
color: var(--text-primary);
}
.phone-shell {
width: 390px;
min-height: 844px;
background: var(--bg-content);
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-status-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
position: relative;
}
.nav-back {
font-size: 18px;
color: var(--text-primary);
cursor: pointer;
line-height: 1;
text-decoration: none;
}
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.nav-action {
margin-left: auto;
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--primary-dark);
text-decoration: none;
font-weight: 600;
}
.scroll-content {
flex: 1;
overflow-y: auto;
background: linear-gradient(180deg, #ecfbfb 0%, #f7fbfb 160px, #f7fbfb 100%);
}
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 14px; }
.report-paper {
background: #fff;
border-radius: 20px;
box-shadow: var(--paper-shadow);
border: 1px solid rgba(32, 199, 199, 0.08);
overflow: hidden;
}
.report-header {
padding: 18px 16px 14px;
background: linear-gradient(180deg, #fbffff 0%, #f4ffff 100%);
border-bottom: 1px solid var(--line);
}
.report-header__top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.report-header__title {
font-size: 20px;
font-weight: 700;
letter-spacing: 1px;
color: #0f7d7d;
margin-bottom: 6px;
}
.report-header__sub {
font-size: 12px;
color: var(--text-muted);
line-height: 1.6;
}
.report-status {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 62px;
height: 28px;
padding: 0 10px;
border-radius: 14px;
background: #f6ffed;
color: var(--success);
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
.summary-strip {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.summary-strip__item {
background: var(--primary-light);
border-radius: 14px;
padding: 10px 8px;
text-align: center;
}
.summary-strip__label {
font-size: 11px;
color: var(--text-muted);
margin-bottom: 4px;
}
.summary-strip__value {
font-size: 15px;
font-weight: 700;
color: var(--primary-dark);
}
.basic-info {
padding: 10px 16px 2px;
}
.basic-info__title,
.report-section__title,
.conclusion-card__title {
height: 30px;
display: flex;
align-items: center;
padding: 0 12px;
margin: 0 -16px 12px;
background: linear-gradient(90deg, #78e3e3 0%, #a4eded 100%);
color: #0c7070;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.5px;
}
.basic-info__grid {
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 12px;
row-gap: 10px;
padding-bottom: 12px;
}
.basic-info__item {
display: flex;
align-items: flex-start;
gap: 8px;
min-width: 0;
font-size: 12px;
line-height: 1.5;
}
.basic-info__label {
color: var(--text-muted);
white-space: nowrap;
flex-shrink: 0;
}
.basic-info__value {
color: var(--text-primary);
font-weight: 600;
min-width: 0;
word-break: break-all;
}
.report-section {
padding: 12px 16px 0;
}
.insight-cards {
padding: 14px 16px 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.insight-card {
border: 1px solid var(--border);
border-radius: 16px;
background: linear-gradient(180deg, #fdffff 0%, #f8ffff 100%);
overflow: hidden;
box-shadow: 0 4px 16px rgba(17, 155, 155, 0.05);
}
.insight-card__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px 12px 10px;
}
.insight-card__title {
font-size: 14px;
font-weight: 700;
color: #0d7878;
}
.insight-card__actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.insight-card__voice {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid #c7eded;
background: #fff;
color: var(--primary-dark);
cursor: pointer;
padding: 0;
}
.insight-card__voice svg { width: 18px; height: 18px; }
.insight-card__voice.is-playing {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.insight-card__content {
position: relative;
padding: 0 12px 12px;
}
.insight-card__text {
font-size: 12px;
line-height: 1.8;
color: #4d4d4d;
max-height: 64px;
overflow: hidden;
transition: max-height 0.25s ease;
}
.insight-card.is-expanded .insight-card__text {
max-height: 320px;
}
.insight-card__fade {
position: absolute;
left: 12px;
right: 12px;
bottom: 40px;
height: 28px;
background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.96) 100%);
pointer-events: none;
}
.insight-card.is-expanded .insight-card__fade {
display: none;
}
.insight-card__toggle {
margin-top: 10px;
display: inline-flex;
align-items: center;
gap: 4px;
border: none;
background: transparent;
color: var(--primary-dark);
font-size: 12px;
font-weight: 600;
cursor: pointer;
padding: 0;
}
.report-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
margin-bottom: 12px;
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
}
.report-table th,
.report-table td {
border: 1px solid var(--border);
padding: 7px 6px;
font-size: 11px;
line-height: 1.45;
text-align: center;
vertical-align: middle;
word-break: break-word;
}
.report-table th {
background: #f2ffff;
color: #0e7f7f;
font-weight: 700;
}
.report-table td:first-child,
.report-table th:first-child {
text-align: left;
padding-left: 8px;
}
.report-table__hint {
color: var(--text-muted);
}
.is-high,
.is-low,
.is-warn {
color: var(--danger);
font-weight: 700;
}
.flag {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 18px;
padding: 0 6px;
border-radius: 9px;
font-size: 10px;
font-weight: 700;
}
.flag--normal {
background: #f6ffed;
color: var(--success);
}
.flag--warn {
background: var(--danger-light);
color: var(--danger);
}
.conclusion-wrap {
padding: 12px 16px 18px;
}
.conclusion-card {
margin-bottom: 14px;
}
.conclusion-card__box {
border: 1px solid var(--border);
border-radius: 14px;
background: #fbffff;
padding: 12px;
font-size: 12px;
line-height: 1.8;
color: #444;
}
.back-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
height: 46px;
border: none;
border-radius: 23px;
background: linear-gradient(135deg, #13c2c2, #36cfc9);
color: #fff;
font-size: 15px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 8px 20px rgba(19, 194, 194, 0.22);
}
.bottom-spacer { height: 20px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 返回按钮:返回体检报告列表 -->
<span class="nav-back" onclick="location.href='reports.html'"></span>
<span class="nav-title">报告查询</span>
<a class="nav-action" href="javascript:void(0)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
下载
</a>
</div>
<div class="scroll-content">
<div class="page-body">
<div class="report-paper">
<div class="report-header">
<div class="report-header__top">
<div>
<div class="report-header__title">2025年体检报告</div>
<div class="report-header__sub">姓名:张旭
<br>性别:男
<br>年龄:32
<br>身份证号:430626198503250024
<br>体检日期:2025-03-21
<br>体检医院:宁夏宝石花医院健康管理中心
</div>
</div>
<div class="report-status">已出报告</div>
</div>
<!-- <div class="summary-strip">-->
<!-- <div class="summary-strip__item">-->
<!-- <div class="summary-strip__label">总体状态</div>-->
<!-- <div class="summary-strip__value">已完成</div>-->
<!-- </div>-->
<!-- <div class="summary-strip__item">-->
<!-- <div class="summary-strip__label">异常项</div>-->
<!-- <div class="summary-strip__value">6 项</div>-->
<!-- </div>-->
<!-- <div class="summary-strip__item">-->
<!-- <div class="summary-strip__label">体检日期</div>-->
<!-- <div class="summary-strip__value">03-15</div>-->
<!-- </div>-->
<!-- </div>-->
</div>
<div class="insight-cards">
<div class="insight-card" id="reportInterpretationCard">
<div class="insight-card__header">
<div class="insight-card__title">体检报告解读</div>
</div>
<div class="insight-card__content">
<div class="insight-card__text">
本次体检整体完成度较高,绝大多数基础指标处于可接受范围。报告中主要需要关注的是体重管理、血脂水平、尿酸结果以及影像检查中提示的轻度脂肪肝和颈椎生理曲度变化。当前结果更偏向生活方式相关问题,暂未见需要立即进一步处置的急性风险信号。建议重点围绕饮食结构优化、规律运动、减少久坐、保证睡眠进行持续干预,并结合 3 个月后复查结果判断改善效果。
</div>
<div class="insight-card__fade"></div>
<!-- [交互] 点击展开/收起体检报告解读 -->
<button class="insight-card__toggle" type="button" onclick="toggleInsight('reportInterpretationCard', this)">展开</button>
</div>
</div>
<div class="insight-card" id="reportConclusionCard">
<div class="insight-card__header">
<div class="insight-card__title">体检结论</div>
<div class="insight-card__actions">
<!-- [交互] 点击切换语音播报按钮状态 -->
<button class="insight-card__voice" type="button" onclick="toggleVoice(this)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>
</button>
</div>
</div>
<div class="insight-card__content">
<div class="insight-card__text">
体检结论显示:BMI 偏高,提示目前处于超重状态;尿酸偏高,需警惕高嘌呤饮食带来的持续影响;血脂中总胆固醇、甘油三酯、低密度脂蛋白存在升高情况,属于代谢类指标异常;彩超提示轻度脂肪肝;颈椎影像提示生理曲度变直。其余血常规、尿常规、血糖、肝功能、甲状腺功能、肿瘤标志物及心电图结果总体平稳。
</div>
<div class="insight-card__fade"></div>
<!-- [交互] 点击展开/收起体检结论 -->
<button class="insight-card__toggle" type="button" onclick="toggleInsight('reportConclusionCard', this)">展开</button>
</div>
</div>
<div class="insight-card" id="reportAdviceCard">
<div class="insight-card__header">
<div class="insight-card__title">体检建议</div>
<div class="insight-card__actions">
<!-- [交互] 点击切换语音播报按钮状态 -->
<button class="insight-card__voice" type="button" onclick="toggleVoice(this)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>
</button>
</div>
</div>
<div class="insight-card__content">
<div class="insight-card__text">
建议近期以减重和代谢指标管理为重点:控制油炸食品、动物内脏、甜食和夜宵摄入,增加蔬菜、粗粮和优质蛋白比例;每周保持不少于 150 分钟中等强度有氧运动,并增加颈肩拉伸训练;每日足量饮水,减少含糖饮料和酒精;保持规律作息,避免长期伏案和久坐;建议 3 个月后复查血脂、尿酸、肝脏彩超等相关项目,动态观察改善情况。
</div>
<div class="insight-card__fade"></div>
<!-- [交互] 点击展开/收起体检建议 -->
<button class="insight-card__toggle" type="button" onclick="toggleInsight('reportAdviceCard', this)">展开</button>
</div>
</div>
</div>
<div class="report-section">
<div class="report-section__title">一般检查</div>
<table class="report-table">
<thead>
<tr><th style="width:30%">项目</th><th style="width:24%">结果</th><th style="width:18%">单位</th><th style="width:28%">参考值</th></tr>
</thead>
<tbody>
<tr><td>身高</td><td>163</td><td>cm</td><td>--</td></tr>
<tr><td>体重</td><td>68.5</td><td>kg</td><td>--</td></tr>
<tr><td>BMI</td><td class="is-high">25.8 ↑</td><td>kg/m²</td><td>18.5-23.9</td></tr>
<tr><td>收缩压</td><td>120</td><td>mmHg</td><td>90-139</td></tr>
<tr><td>舒张压</td><td>78</td><td>mmHg</td><td>60-89</td></tr>
<tr><td>脉搏</td><td>76</td><td>次/分</td><td>60-100</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">内科</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>检查结果</th><th>单位</th><th>参考值</th></tr>
</thead>
<tbody>
<tr><td>心率</td><td>76</td><td>次/分</td><td>60-100</td></tr>
<tr><td>心律</td><td></td><td>--</td><td>--</td></tr>
<tr><td>心音</td><td>未见异常</td><td>--</td><td>--</td></tr>
<tr><td>呼吸系统</td><td>双肺呼吸音清</td><td>--</td><td>--</td></tr>
<tr><td>腹部触诊</td><td>未见异常</td><td>--</td><td>--</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">外科</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>检查结果</th><th>单位</th><th>参考值</th></tr>
</thead>
<tbody>
<tr><td>甲状腺</td><td>未触及肿大</td><td>--</td><td>--</td></tr>
<tr><td>浅表淋巴结</td><td>未见异常</td><td>--</td><td>--</td></tr>
<tr><td>脊柱</td><td>未见异常</td><td>--</td><td>--</td></tr>
<tr><td>四肢关节</td><td>活动可</td><td>--</td><td>--</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">血常规</div>
<table class="report-table">
<thead>
<tr><th style="width:32%">项目</th><th style="width:22%">结果</th><th style="width:18%">单位</th><th style="width:28%">参考范围</th></tr>
</thead>
<tbody>
<tr><td>白细胞计数 WBC</td><td>6.80</td><td>10^9/L</td><td>3.5-9.5</td></tr>
<tr><td>红细胞计数 RBC</td><td>4.52</td><td>10^12/L</td><td>3.8-5.1</td></tr>
<tr><td>血红蛋白 HGB</td><td>136</td><td>g/L</td><td>115-150</td></tr>
<tr><td>红细胞压积 HCT</td><td>40.5</td><td>%</td><td>35-45</td></tr>
<tr><td>血小板计数 PLT</td><td>248</td><td>10^9/L</td><td>125-350</td></tr>
<tr><td>中性粒细胞比率</td><td>58.6</td><td>%</td><td>40-75</td></tr>
<tr><td>淋巴细胞比率</td><td>33.2</td><td>%</td><td>20-50</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">尿常规</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>尿蛋白 PRO</td><td>阴性</td><td>--</td><td>阴性</td></tr>
<tr><td>尿糖 GLU</td><td>阴性</td><td>--</td><td>阴性</td></tr>
<tr><td>酮体 KET</td><td>阴性</td><td>--</td><td>阴性</td></tr>
<tr><td>潜血 BLD</td><td>阴性</td><td>--</td><td>阴性</td></tr>
<tr><td>白细胞 LEU</td><td>阴性</td><td>--</td><td>阴性</td></tr>
<tr><td>尿比重 SG</td><td>1.020</td><td>--</td><td>1.005-1.030</td></tr>
<tr><td>尿酸碱度 pH</td><td>6.0</td><td>--</td><td>5.0-8.0</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">肝功能</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>丙氨酸氨基转移酶 ALT</td><td>24.6</td><td>U/L</td><td>7-40</td></tr>
<tr><td>天门冬氨酸氨基转移酶 AST</td><td>22.4</td><td>U/L</td><td>13-35</td></tr>
<tr><td>总蛋白 TP</td><td>74.8</td><td>g/L</td><td>65-85</td></tr>
<tr><td>白蛋白 ALB</td><td>44.2</td><td>g/L</td><td>40-55</td></tr>
<tr><td>总胆红素 TBIL</td><td>12.1</td><td>μmol/L</td><td>5-21</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">肾功能</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>尿素 UREA</td><td>4.8</td><td>mmol/L</td><td>2.8-7.2</td></tr>
<tr><td>肌酐 CREA</td><td>71</td><td>μmol/L</td><td>41-73</td></tr>
<tr><td>尿酸 UA</td><td class="is-high">389 ↑</td><td>μmol/L</td><td>155-357</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">血脂</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>总胆固醇 TC</td><td class="is-high">6.10 ↑</td><td>mmol/L</td><td>3.1-5.7</td></tr>
<tr><td>甘油三酯 TG</td><td class="is-high">2.08 ↑</td><td>mmol/L</td><td>0.4-1.7</td></tr>
<tr><td>高密度脂蛋白 HDL-C</td><td>1.18</td><td>mmol/L</td><td>1.04-1.55</td></tr>
<tr><td>低密度脂蛋白 LDL-C</td><td class="is-high">3.92 ↑</td><td>mmol/L</td><td>0-3.4</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">血糖</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>空腹血糖 GLU</td><td>5.20</td><td>mmol/L</td><td>3.9-6.1</td></tr>
<tr><td>糖化血红蛋白 HbA1c</td><td>5.6</td><td>%</td><td>4.0-6.0</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">甲状腺功能</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>促甲状腺激素 TSH</td><td>2.26</td><td>mIU/L</td><td>0.27-4.20</td></tr>
<tr><td>游离三碘甲状腺原氨酸 FT3</td><td>4.88</td><td>pmol/L</td><td>3.1-6.8</td></tr>
<tr><td>游离甲状腺素 FT4</td><td>15.4</td><td>pmol/L</td><td>12-22</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">肿瘤标志物</div>
<table class="report-table">
<thead>
<tr><th>项目</th><th>结果</th><th>单位</th><th>参考范围</th></tr>
</thead>
<tbody>
<tr><td>癌胚抗原 CEA</td><td>1.82</td><td>ng/mL</td><td>0-5.0</td></tr>
<tr><td>甲胎蛋白 AFP</td><td>3.44</td><td>ng/mL</td><td>0-8.1</td></tr>
<tr><td>糖类抗原 CA125</td><td>18.6</td><td>U/mL</td><td>0-35</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">心电图</div>
<table class="report-table">
<thead>
<tr><th style="width:30%">项目</th><th style="width:36%">结果</th><th style="width:14%">单位</th><th style="width:20%">参考</th></tr>
</thead>
<tbody>
<tr><td>十二导联心电图</td><td>窦性心律</td><td>--</td><td>--</td></tr>
<tr><td>心电图提示</td><td>未见明显异常</td><td>--</td><td>--</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">彩超检查</div>
<table class="report-table">
<thead>
<tr><th>检查项目</th><th>检查结果</th><th>单位</th><th>参考</th></tr>
</thead>
<tbody>
<tr><td>肝胆脾胰双肾彩超</td><td>轻度脂肪肝</td><td>--</td><td>--</td></tr>
<tr><td>甲状腺彩超</td><td>甲状腺双叶回声均匀</td><td>--</td><td>--</td></tr>
<tr><td>乳腺彩超</td><td>未见明显异常</td><td>--</td><td>--</td></tr>
</tbody>
</table>
</div>
<div class="report-section">
<div class="report-section__title">影像检查</div>
<table class="report-table">
<thead>
<tr><th>检查项目</th><th>检查结果</th><th>单位</th><th>参考</th></tr>
</thead>
<tbody>
<tr><td>胸部 DR</td><td>心肺膈未见明显异常</td><td>--</td><td>--</td></tr>
<tr><td>颈椎 CT</td><td>颈椎生理曲度变直</td><td>--</td><td>--</td></tr>
</tbody>
</table>
</div>
<div class="conclusion-wrap">
<!-- [交互] 返回报告列表按钮 -->
<button class="back-btn" onclick="location.href='reports.html'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
返回报告列表
</button>
</div>
</div>
<div class="bottom-spacer"></div>
</div>
</div>
</div>
<script>
// [交互] 展开/收起洞察卡片
function toggleInsight(cardId, btn) {
var card = document.getElementById(cardId);
var content = card.querySelector('.insight-card__content');
var fade = card.querySelector('.insight-card__fade');
var isExpanded = card.classList.toggle('is-expanded');
btn.textContent = isExpanded ? '收起' : '展开';
if (fade) fade.style.display = isExpanded ? 'none' : '';
}
// [交互] 语音播报按钮切换(原型演示,仅切换状态)
function toggleVoice(btn) {
btn.classList.toggle('is-playing');
}
</script>
</body>
</html>
+157
View File
@@ -0,0 +1,157 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体检报告</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
.phone-shell {
width: 390px; min-height: 844px; background: #f7f9fb; border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.phone-status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.phone-status-bar .time { font-size: 15px; font-weight: 700; }
.phone-status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; display: flex; align-items: center; padding: 0 16px;
background: #fff; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; position: relative;
}
.nav-back { font-size: 18px; color: #1a1a1a; cursor: pointer; line-height: 1; text-decoration: none; }
.nav-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: #1a1a1a; }
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #f3fbfb 0%, #f7f9fb 160px, #f7f9fb 100%); }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 顶部 AI 解读大卡片 */
.ai-card {
background: linear-gradient(135deg, #ffffff 0%, #e6fffb 100%);
border: 1px solid #b5f5ec; border-radius: 20px; padding: 20px 18px;
box-shadow: 0 6px 18px rgba(20, 180, 176, 0.10); position: relative; overflow: hidden;
}
.ai-card::after {
content: ''; position: absolute; right: -30px; top: -30px; width: 120px; height: 120px;
border-radius: 50%; background: rgba(19, 194, 194, 0.08);
}
.ai-card__badge {
display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px;
border-radius: 13px; background: rgba(19, 194, 194, 0.12); color: #08979c;
font-size: 12px; font-weight: 600; margin-bottom: 10px;
}
.ai-card__title { font-size: 20px; font-weight: 700; color: #1a1a1a; margin-bottom: 4px; position: relative; z-index: 1; }
.ai-card__sub { font-size: 13px; color: #13c2c2; font-weight: 600; margin-bottom: 8px; position: relative; z-index: 1; }
.ai-card__desc { font-size: 13px; line-height: 1.7; color: #666; position: relative; z-index: 1; }
/* 报告列表 */
.section-title { font-size: 16px; font-weight: 700; color: #1a1a1a; margin: 22px 2px 12px; }
.report-list { display: flex; flex-direction: column; gap: 12px; }
.report-item {
background: #fff; border-radius: 16px; padding: 16px; display: flex; align-items: center; gap: 14px;
box-shadow: 0 4px 14px rgba(0,0,0,0.05); border: 1px solid #f0f0f0; cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.report-item:active { transform: scale(0.985); }
.report-item__icon {
width: 48px; height: 48px; border-radius: 14px; display: flex; align-items: center;
justify-content: center; flex-shrink: 0; background: #fff7e6; color: #fa8c16;
}
.report-item__content { flex: 1; min-width: 0; }
.report-item__title { font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 4px; }
.report-item__meta { font-size: 12px; color: #8c8c8c; }
.report-item__arrow { font-size: 18px; color: #c6c6c6; flex-shrink: 0; }
.bottom-spacer { height: 28px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="phone-status-bar">
<span class="time">12:00</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<!-- [交互] 返回按钮:返回健康体检主页 -->
<span class="nav-back" onclick="location.href='index.html'"></span>
<span class="nav-title">体检报告</span>
</div>
<div class="scroll-content">
<div class="page-body">
<!-- 顶部 AI 解读大卡片 -->
<div class="ai-card">
<div class="ai-card__badge">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<span>AI解读</span>
</div>
<div class="ai-card__title">体检报告解读</div>
<div class="ai-card__desc">依托 AI 技术,自动解析体检报告数据,提供专业指标解读、风险评估与个性化健康指导。</div>
<br>
<div class="ai-card__sub">查看报告解读</div>
</div>
<!-- 报告列表 -->
<div class="section-title">我的历年报告</div>
<div class="report-list">
<!-- [交互] 点击跳转至: report-query.html -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="report-item__content">
<div class="report-item__title">2025年度胃肠镜体检报告</div>
<div class="report-item__meta">长庆星龙园医院</div>
</div>
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="report-item__content">
<div class="report-item__title">2025年度常规体检报告</div>
<div class="report-item__meta">长庆星龙园医院</div>
</div>
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="report-item__content">
<div class="report-item__title">2024年度常规体检报告</div>
<div class="report-item__meta">长庆星龙园医院</div>
</div>
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
<div class="report-item__content">
<div class="report-item__title">2023年度常规体检报告</div>
<div class="report-item__meta">长庆星龙园医院</div>
</div>
<span class="report-item__arrow"></span>
</div>
</div>
<div class="bottom-spacer"></div>
</div>
</div>
</div>
</body>
</html>
+1 -16
View File
@@ -154,7 +154,7 @@
<!-- [交互] 导航栏:返回按钮 + 标题 + 右侧消息图标 -->
<div class="nav-bar">
<span class="nav-back" onclick="goBackFromSource()">← 返回</span>
<span class="nav-back" onclick="location.href='../home/index.html'">← 返回</span>
<span class="nav-title">健康咨询</span>
</div>
@@ -261,20 +261,5 @@
<div class="bottom-spacer"></div>
</div><!-- /.phone-shell -->
<script>
function goBackFromSource() {
var params = new URLSearchParams(window.location.search);
var from = params.get('from');
if (from === 'index') {
window.location.href = '../home/index.html';
return;
}
if (document.referrer) {
history.back();
return;
}
window.location.href = '../home/index.html';
}
</script>
</body>
</html>
@@ -157,7 +157,7 @@
<!-- 温馨提示 -->
<div class="tip-box">📌 温馨提示:请在预约时间前5分钟准备好,保持网络畅通,等待医生拨打视频通话</div>
<div class="tip-box">📌 温馨提示:请在预约时间保持网络畅通,等待医生拨打视频通话</div>
</div>
<!-- [交互] 底部操作栏 -->
@@ -0,0 +1,329 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>血氧预警历史 - 健康监测 | 健康长庆原型</title>
<style>
/* 全局重置 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* 手机外壳 */
.phone-shell {
width: 390px;
height: 844px;
background: #f5f7fa;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* 状态栏 */
.status-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
background: #fff;
}
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
padding: 0 16px;
flex-shrink: 0;
border-bottom: 1px solid #f0f0f0;
}
.nav-back {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-right: 28px;
}
/* 滚动内容区 */
.scroll-content {
flex: 1;
overflow-y: auto;
background: #f5f7fa;
}
.scroll-content::-webkit-scrollbar { display: none; }
/* 预警统计卡片 */
.stats-card {
margin: 12px 12px 0;
background: #fff;
border-radius: 14px;
padding: 20px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.stats-card__left { display: flex; flex-direction: column; align-items: flex-start; }
.stats-card__label { font-size: 13px; color: #999; margin-bottom: 6px; }
.stats-card__count-row { display: flex; align-items: baseline; gap: 4px; }
/* 本月无预警,数字显示绿色 */
.stats-card__count { font-size: 40px; font-weight: 800; color: #52c41a; line-height: 1; }
.stats-card__unit { font-size: 14px; color: #52c41a; font-weight: 600; }
.stats-card__right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.stats-card__recent-label { font-size: 12px; color: #999; }
.stats-card__recent-time { font-size: 13px; font-weight: 600; color: #333; }
/* 预警图标装饰 */
.stats-card__icon {
width: 48px;
height: 48px;
background: #f6ffed;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
/* 列表区块标题 */
.section-title {
font-size: 13px;
color: #999;
padding: 14px 16px 8px;
font-weight: 500;
}
/* 预警记录卡片 */
.alert-card {
margin: 0 12px 10px;
background: #fff;
border-radius: 14px;
padding: 14px 14px 14px 12px;
display: flex;
gap: 12px;
align-items: flex-start;
}
/* 左侧等级圆点 */
.alert-card__dot-col {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3px;
gap: 4px;
}
.alert-card__dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
/* 严重:红色 */
.alert-card__dot--critical { background: #ff4d4f; box-shadow: 0 0 0 3px rgba(255,77,79,0.18); }
/* 卡片主体内容 */
.alert-card__body { flex: 1; min-width: 0; }
.alert-card__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.alert-card__time { font-size: 13px; color: #666; }
.alert-card__badge {
font-size: 11px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
flex-shrink: 0;
}
/* 严重标签:红底红字 */
.alert-card__badge--critical { background: #fff1f0; color: #ff4d4f; }
/* 监测值 */
.alert-card__value-row {
display: flex;
align-items: baseline;
gap: 4px;
margin-bottom: 6px;
}
.alert-card__value { font-size: 24px; font-weight: 800; color: #1a1a1a; line-height: 1; }
.alert-card__value-unit { font-size: 13px; color: #999; }
/* 详情行 */
.alert-card__detail-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.alert-card__detail-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #999;
}
.alert-card__detail-item svg { flex-shrink: 0; }
/* 触发原因 */
.alert-card__reason {
margin-top: 8px;
font-size: 12px;
color: #bbb;
background: #fafafa;
border-radius: 6px;
padding: 6px 8px;
line-height: 1.5;
}
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<!-- WiFi 图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
<circle cx="12" cy="20" r="1" fill="#333" stroke="none"/>
</svg>
<!-- 电池图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<rect x="1" y="6" width="18" height="12" rx="2"/>
<path d="M21 10v4"/>
<rect x="3" y="8" width="12" height="8" rx="1" fill="#333" stroke="none"/>
</svg>
</div>
</div>
<!-- 导航栏:返回血氧详情页 -->
<div class="nav-bar">
<div class="nav-back" onclick="location.href='blood-oxygen.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round">
<path d="M15 19l-7-7 7-7"/>
</svg>
</div>
<div class="nav-title">血氧预警历史</div>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">0</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 血氧水滴图标,绿色背景表示正常 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M12 2C12 2 5 9.5 5 14a7 7 0 0 0 14 0C19 9.5 12 2 12 2z" fill="#52c41a" opacity="0.85"/>
<path d="M9 13.5a3 3 0 0 0 3 3" stroke="#fff" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">暂无</div>
</div>
</div>
<!-- 空状态:本月无预警记录 -->
<!-- <div class="empty-state">-->
<!-- &lt;!&ndash; 大图标:绿色盾牌表示健康安全 &ndash;&gt;-->
<!-- <div class="empty-state__icon">-->
<!-- <svg width="40" height="40" viewBox="0 0 24 24" fill="none">-->
<!-- <path d="M12 2L3 7v5c0 5.25 3.75 10.15 9 11.35C17.25 22.15 21 17.25 21 12V7L12 2z" fill="#52c41a" opacity="0.15" stroke="#52c41a" stroke-width="1.5" stroke-linejoin="round"/>-->
<!-- &lt;!&ndash; 对勾 &ndash;&gt;-->
<!-- <polyline points="9 12 11 14 15 10" stroke="#52c41a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>-->
<!-- </svg>-->
<!-- </div>-->
<!-- <div class="empty-state__text">暂无预警记录,请继续保持</div>-->
<!-- </div>-->
<!-- 预警记录列表标题 -->
<div class="section-title">预警记录(共 1 条)</div>
<!-- 历史预警记录:2026-03-08 严重 -->
<div class="alert-card">
<!-- 左侧严重等级红点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 严重等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-03-08 03:22</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 血氧监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">93</span>
<span class="alert-card__value-unit">%</span>
</div>
<!-- 持续时长 + 血氧标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 8 分钟</span>
</div>
<!-- 血氧水滴图标 + 标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C12 2 6 9 6 14a6 6 0 0 0 12 0C18 9 12 2 12 2z"/>
</svg>
<span>血氧</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:血氧低于 95% 阈值</div>
</div>
</div>
<!-- 底部安全区 -->
<div class="bottom-safe"></div>
</div><!-- /scroll-content -->
</div><!-- /phone-shell -->
</body>
</html>
@@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>血氧详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 主题色(血氧绿) */
:root { --theme: #52c41a; --theme2: #95de64; }
.top-panel { background: #fff; padding-top: 2px; }
.time-tabs { display: flex; justify-content: center; gap: 42px; padding: 2px 0 8px; }
.time-tab { position: relative; font-size: 18px; color: #444; cursor: pointer; padding: 0 2px 6px; user-select: none; }
.time-tab.active { color: var(--theme); }
.time-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; margin: auto; width: 22px; height: 3px; border-radius: 2px; background: var(--theme); }
.summary-area { text-align: center; padding: 12px 24px 6px; }
.date-row { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; color: #666; font-weight: 500; }
.range-text { margin-top: 4px; font-size: 16px; color: #666; }
.main-row { margin-top: 2px; display: inline-flex; align-items: baseline; gap: 4px; }
.main-row__value { font-size: 48px; line-height: 1; font-weight: 500; color: #1f1f1f; letter-spacing: -1px; }
.main-row__unit { font-size: 18px; color: #333; }
.main-badge { display: inline-flex; align-items: center; justify-content: center; margin-top: 6px; padding: 3px 12px; border-radius: 18px; font-size: 12px; font-weight: 600; background: #f6ffed; color: #52c41a; }
.chart-wrap { position: relative; background: #fff; padding: 6px 16px 0; }
.chart-box { position: relative; height: 160px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: var(--theme); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: var(--theme); }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 0; height: 24px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 10px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: -4px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.card { margin: 8px 12px 0; background: #fff; border-radius: 22px; padding: 14px 12px; display: flex; gap: 0; }
.metric-row { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 6px; }
.metric-left__label { font-size: 12px; color: #777; margin-bottom: 4px; }
.metric-left__value { display: flex; align-items: baseline; gap: 2px; }
.metric-left__num { font-size: 16px; font-weight: 600; color: #222; }
.metric-left__unit { font-size: 12px; color: #777; }
.metric-right { font-size: 12px; color: #777; text-align: center; }
.metric-split { width: 1px; background: #ececec; margin: 0; }
.alert-btn { margin: 8px 12px 0; border: 1.5px solid var(--theme); border-radius: 12px; padding: 11px; text-align: center; font-size: 13px; color: var(--theme); font-weight: 600; cursor: pointer; background: #fff; display: flex; align-items: center; justify-content: center; gap: 6px; }
.foot-note { padding: 10px 0 12px; text-align: center; font-size: 11px; color: #b3b3b3; }
.bottom-safe { height: 12px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">血氧</div>
</div>
<div class="scroll-content">
<div class="top-panel">
<div class="time-tabs">
<div class="time-tab active" data-mode="day"></div>
<div class="time-tab" data-mode="week"></div>
<div class="time-tab" data-mode="month"></div>
<div class="time-tab" data-mode="year"></div>
</div>
<div class="summary-area">
<div class="date-row">
<span id="dateText">4月30日 周四</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="#666"><path d="M7 10l5 5 5-5z"/></svg>
</div>
<div class="range-text" id="rangeText">00:00-23:59</div>
<div class="main-row">
<span class="main-row__value" id="mainValue">98</span>
<span class="main-row__unit" id="mainUnit">%</span>
</div>
<div class="main-badge" id="statusBadge">正常</div>
</div>
<div class="chart-wrap">
<div class="chart-box">
<svg class="chart-svg" id="chart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="line" class="chart-path"></path>
<circle id="dot" class="chart-dot" r="3.2"></circle>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
</div>
</div>
</div>
<div class="card">
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最高</div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">99</span><span class="metric-left__unit">%</span></div>
</div>
<div class="metric-right" id="maxHint">10:10</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最低</div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">95</span><span class="metric-left__unit">%</span></div>
</div>
<div class="metric-right" id="minHint">02:50</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">平均</div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">97</span><span class="metric-left__unit">%</span></div>
</div>
<div class="metric-right" id="avgHint">全天</div>
</div>
</div>
<div class="alert-btn" onclick="location.href='blood-oxygen-alerts.html'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--theme)" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
<div class="foot-note">以上数据为原型示例,实际以设备同步为准</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
unit: '%',
decimals: 0,
points: [97,97,96,96,95,96,96,97,98,98,97,97,98,99,98,97,97,96,97,98,98,97,97,97],
totalMode: 'last',
yTicks: [100, 98, 96, 94],
xLabels: ['0:00','6:00','12:00','18:00','24:00'],
status: '正常',
max: { v:99, hint:'10:10' },
min: { v:95, hint:'02:50' },
avg: { v:97, hint:'全天' }
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: '%',
decimals: 0,
points: [97,96,98,97,97,98,98],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
xLabels: ['周一','周二','周三','周四','周五','周六','周日'],
status: '正常',
max: { v:98, hint:'周三' },
min: { v:96, hint:'周二' },
avg: { v:97, hint:'本周' }
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: '%',
decimals: 0,
points: [97,97,96,96,95,96,96,97,98,98,97,97,98,99,98,97,97,96,97,98,98,97,97,97,97,97,96,96,95,96],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
xLabels: ['1日','7日','14日','21日','30日'],
status: '正常',
max: { v:99, hint:'04-14' },
min: { v:95, hint:'04-05' },
avg: { v:97, hint:'本月' }
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: '%',
decimals: 0,
points: [97,97,96,96,97,96,96,97,98,97,96,97],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
status: '正常',
max: { v:98, hint:'01月' },
min: { v:96, hint:'03月' },
avg: { v:97, hint:'全年' }
}
};
function avg(arr) {
if (!arr.length) return 0;
var sum = arr.reduce(function(s, v){ return s + v; }, 0);
return sum / arr.length;
}
function getValue(cfg) {
if (cfg.totalMode === 'last') return cfg.points[cfg.points.length - 1];
if (cfg.totalMode === 'avg') return avg(cfg.points);
if (cfg.totalMode === 'max') return Math.max.apply(null, cfg.points);
if (cfg.totalMode === 'min') return Math.min.apply(null, cfg.points);
return cfg.points[cfg.points.length - 1];
}
function formatValue(val, decimals) {
if (typeof val !== 'number') return String(val);
if (decimals != null) return val.toFixed(decimals);
return String(val);
}
function renderChart(cfg) {
var chartWidth = 330;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('line');
var dotEl = document.getElementById('dot');
grid.innerHTML = '';
cfg.yTicks.forEach(function(_, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(tick, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatValue(tick, 0) + '</span>';
}).join('');
var pts = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var d = pts.map(function(p, index) {
return (index === 0 ? 'M' : 'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = pts[pts.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
xAxis.innerHTML = cfg.xLabels.map(function(label) {
return '<span>' + label + '</span>';
}).join('');
}
function renderMode(mode) {
var cfg = dataMap[mode];
var value = getValue(cfg);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, cfg.decimals);
document.getElementById('maxHint').textContent = cfg.max.hint;
document.getElementById('minValue').textContent = formatValue(cfg.min.v, cfg.decimals);
document.getElementById('minHint').textContent = cfg.min.hint;
document.getElementById('avgValue').textContent = formatValue(cfg.avg.v, cfg.decimals);
document.getElementById('avgHint').textContent = cfg.avg.hint;
renderChart(cfg);
}
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
};
});
renderMode('day');
</script>
</body>
</html>
@@ -0,0 +1,87 @@
# 健康监测 - 变更记录
> 本文档记录「健康监测」模块原型的所有变更,按时间倒序排列。
---
## [2026-05-07] refactor: 统一四个预警历史页面布局结构
**涉及文件(4个修改):**
- `blood-oxygen-alerts.html` — 更新 stats-card 结构,添加 stats-card__recent-label 和 stats-card__recent-time,删除 stats-card__sub-label;更新 section-title 文本为"预警记录(共 1 条)"
- `temperature-alerts.html` — 完全重构为标准格式,采用与 heart-rate-alerts.html 相同的 stats-card 和 alert-card 结构,颜色主题使用黄色(#faad14
- `stress-alerts.html` — 已符合标准格式(无需修改)
- `heart-rate-alerts.html` — 参考标准(无需修改)
**变更说明:**
- 四个预警历史页面现采用统一的布局结构:stats-card(左侧统计数据 + 右侧图标和最近预警时间)+ alert-card(左侧等级圆点 + 右侧卡片内容)
- 各页面保持各自的颜色主题:心率红色(#ff7a8a)、血氧绿色(#52c41a)、体温黄色(#faad14)、压力橙色(#fa8c16
- 统一的 CSS 类名和 DOM 结构便于后续维护和扩展
---
## [2026-04-30] refactor: 压缩四个详情页面整体布局高度
**涉及文件(4个修改):**
- `heart-rate.html` — 减少主值字体大小(58px → 48px)、图表高度(258px → 160px)、卡片内边距(18px 12px → 14px 12px
- `temperature.html` — 同步应用布局压缩
- `blood-oxygen.html` — 同步应用布局压缩
- `stress.html` — 同步应用布局压缩
**变更说明:**
- 所有详情页面现可在 844px 手机屏幕内完整显示,无需垂直滚动
- 减少了 150+ 像素的垂直空间占用
---
## [2026-04-30] refactor: 详情页面最高/最低/平均改为一行三列显示
**涉及文件(4个修改):**
- `heart-rate.html` — 将三个指标从垂直堆叠改为水平三列布局,使用 flex: 1 实现等宽分布
- `temperature.html` — 同步应用三列布局
- `blood-oxygen.html` — 同步应用三列布局
- `stress.html` — 同步应用三列布局
**变更说明:**
- 指标卡片从竖向排列改为横向三列,每列包含标签、数值、单位和时间提示
- 使用垂直分隔线(1px 灰色)分隔三列
- 提高了空间利用效率
---
## [2026-04-27] refactor: 血氧和压力详情页现代化设计
**涉及文件(2个修改):**
- `blood-oxygen.html` — 从旧的 hero-card 布局改为现代 top-panel 结构,新增 CSS 变量(--theme: #52c41a)、Tab 导航、图表、时间模式切换
- `stress.html` — 从旧的 hero-card 布局改为现代 top-panel 结构,新增 CSS 变量(--theme: #faad14)、Tab 导航、图表、时间模式切换
**变更说明:**
- 采用与 temperature.html 相同的现代设计风格
- 新增日/周/月/年四种时间维度的数据展示
- 图表使用 SVG 绘制,支持动态数据切换
- 保留各指标的特定说明文案(血氧正常范围、压力等级说明)
---
## [2026-04-21] init: 健康监测模块初始化
**新增文件(10个):**
| 文件 | 说明 |
|------|------|
| `index.html` | 模块首页:四个监测指标入口 |
| `heart-rate.html` | 心率详情:日/周/月/年数据展示 + 预警历史入口 |
| `heart-rate-alerts.html` | 心率预警历史:预警统计 + 预警记录列表 |
| `blood-oxygen.html` | 血氧详情:日/周/月/年数据展示 + 预警历史入口 |
| `blood-oxygen-alerts.html` | 血氧预警历史:预警统计 + 预警记录列表 |
| `temperature.html` | 体温详情:日/周/月/年数据展示 + 预警历史入口 |
| `temperature-alerts.html` | 体温预警历史:预警统计 + 预警记录列表 |
| `stress.html` | 压力详情:日/周/月/年数据展示 + 预警历史入口 |
| `stress-alerts.html` | 压力预警历史:预警统计 + 预警记录列表 |
| `exercise.html` | 运动详情:日/周/月/年数据展示 |
| `sleep.html` | 睡眠详情:日/周/月/年数据展示 |
**变更说明:**
- 健康监测模块包含 6 个主要监测指标(心率、血氧、体温、压力、运动、睡眠)
- 每个指标支持多时间维度数据展示(日/周/月/年)
- 前 4 个指标配备预警历史页面,展示预警统计和详细记录
- 所有页面采用统一的设计规范和交互模式
+670
View File
@@ -0,0 +1,670 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>锻炼详情 - 健康监测 | 健康长庆原型</title>
<style>
/* ===== 全局重置与基础样式 ===== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
background: #e5e5e5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* ===== 手机外壳 ===== */
.phone-shell {
width: 390px;
height: 844px;
background: #f5f7fa;
border-radius: 44px;
overflow: hidden;
position: relative;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
/* ===== 状态栏 ===== */
.status-bar {
height: 44px;
background: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
flex-shrink: 0;
}
.status-bar .time {
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
}
/* 状态栏右侧图标组 */
.status-icons {
display: flex;
align-items: center;
gap: 6px;
}
/* WiFi 图标(内联SVG */
.icon-wifi {
width: 16px;
height: 12px;
}
/* 电池图标(内联SVG */
.icon-battery {
width: 25px;
height: 12px;
}
/* ===== 导航栏 ===== */
.nav-bar {
height: 44px;
background: #ffffff;
display: flex;
align-items: center;
padding: 0 16px;
position: relative;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
}
/* 返回按钮 */
.nav-back {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
color: #fa8c16;
font-size: 16px;
text-decoration: none;
z-index: 1;
}
.nav-back svg {
width: 10px;
height: 17px;
}
/* 导航标题居中 */
.nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
}
/* ===== 滚动内容区 ===== */
.scroll-content {
height: calc(844px - 44px - 44px);
overflow-y: auto;
padding: 16px 16px 20px;
/* 隐藏滚动条但保留滚动功能 */
-ms-overflow-style: none;
scrollbar-width: none;
}
.scroll-content::-webkit-scrollbar {
display: none;
}
/* ===== 通用卡片样式 ===== */
.card {
background: #ffffff;
border-radius: 16px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.card-title {
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 14px;
}
/* ===== 本周概览卡片 ===== */
.overview-card {
background: linear-gradient(135deg, #fa8c16 0%, #ff6b00 100%);
border-radius: 16px;
padding: 18px 16px;
margin-bottom: 12px;
box-shadow: 0 4px 16px rgba(250, 140, 22, 0.35);
}
.overview-card .card-title {
color: rgba(255, 255, 255, 0.9);
margin-bottom: 16px;
}
/* 概览统计四格布局 */
.overview-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.stat-item {
background: rgba(255, 255, 255, 0.18);
border-radius: 12px;
padding: 12px;
text-align: center;
}
.stat-value {
font-size: 20px;
font-weight: 700;
color: #ffffff;
line-height: 1.2;
}
.stat-unit {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
margin-left: 2px;
}
.stat-label {
font-size: 12px;
color: rgba(255, 255, 255, 0.75);
margin-top: 4px;
}
/* ===== 时间维度 Tab ===== */
.tab-bar {
background: #ffffff;
border-radius: 16px;
padding: 6px;
margin-bottom: 12px;
display: flex;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.tab-item {
flex: 1;
text-align: center;
padding: 8px 0;
font-size: 14px;
font-weight: 500;
color: #8c8c8c;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s;
}
/* 激活状态的 Tab */
.tab-item.active {
background: #fa8c16;
color: #ffffff;
font-weight: 600;
}
/* ===== 锻炼记录列表 ===== */
.section-title {
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 10px;
padding-left: 2px;
}
/* 单条锻炼记录卡片 */
.exercise-card {
background: #ffffff;
border-radius: 16px;
padding: 14px 16px;
margin-bottom: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
/* 记录卡片顶部:图标+名称 + 日期时间 */
.exercise-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.exercise-type {
display: flex;
align-items: center;
gap: 8px;
}
/* 运动类型图标背景圆 */
.exercise-icon {
width: 36px;
height: 36px;
border-radius: 50%;
background: #fff7e6;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.exercise-name {
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
}
.exercise-datetime {
font-size: 12px;
color: #8c8c8c;
}
/* 记录卡片底部:四项数据指标 */
.exercise-metrics {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
padding-top: 12px;
border-top: 1px solid #f5f5f5;
}
.metric-item {
text-align: center;
}
.metric-value {
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
}
.metric-unit {
font-size: 11px;
color: #8c8c8c;
}
.metric-label {
font-size: 11px;
color: #8c8c8c;
margin-top: 2px;
}
/* 心率数值用橙色高亮 */
.metric-value.heart-rate {
color: #fa8c16;
}
/* ===== 锻炼趋势柱状图 ===== */
.trend-card {
background: #ffffff;
border-radius: 16px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
/* 柱状图容器 */
.bar-chart {
display: flex;
align-items: flex-end;
justify-content: space-around;
height: 100px;
padding: 0 8px;
margin-bottom: 8px;
}
/* 单根柱子组(柱体+标签) */
.bar-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
flex: 1;
}
/* 柱体数值标签 */
.bar-value-label {
font-size: 12px;
font-weight: 600;
color: #fa8c16;
}
/* 柱体本身 */
.bar {
width: 32px;
border-radius: 6px 6px 0 0;
background: #ffe7ba;
transition: height 0.3s ease;
}
/* 当前周(最后一根)高亮 */
.bar.current {
background: #fa8c16;
}
/* X轴周标签 */
.bar-x-labels {
display: flex;
justify-content: space-around;
padding: 0 8px;
}
.bar-x-label {
flex: 1;
text-align: center;
font-size: 11px;
color: #8c8c8c;
}
.bar-x-label.current {
color: #fa8c16;
font-weight: 600;
}
/* ===== 统计摘要三卡片 ===== */
.summary-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 12px;
}
.summary-card {
background: #ffffff;
border-radius: 14px;
padding: 14px 10px;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.summary-icon {
font-size: 22px;
margin-bottom: 6px;
}
.summary-value {
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
line-height: 1.2;
}
.summary-sub {
font-size: 11px;
color: #8c8c8c;
margin-top: 2px;
}
.summary-label {
font-size: 11px;
color: #8c8c8c;
margin-top: 4px;
}
/* ===== 底部安全区 ===== */
.bottom-safe {
height: 20px;
}
</style>
</head>
<body>
<!-- 手机外壳 -->
<div class="phone-shell">
<!-- 状态栏:时间 + WiFi + 电池 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="status-icons">
<!-- WiFi 图标 -->
<svg class="icon-wifi" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 9.5C8.69 9.5 9.25 10.06 9.25 10.75S8.69 12 8 12s-1.25-.56-1.25-1.25S7.31 9.5 8 9.5z" fill="#1a1a1a"/>
<path d="M8 6.5c1.38 0 2.63.56 3.54 1.46L12.95 6.55A7.47 7.47 0 008 4.5c-1.93 0-3.69.73-5.01 1.93l1.41 1.41A5.47 5.47 0 018 6.5z" fill="#1a1a1a"/>
<path d="M8 2.5c2.76 0 5.26 1.12 7.07 2.93L16.5 4A9.97 9.97 0 008 .5C5.24.5 2.74 1.62.93 3.43L2.36 4.86A7.97 7.97 0 018 2.5z" fill="#1a1a1a" opacity="0.4"/>
</svg>
<!-- 电池图标 -->
<svg class="icon-battery" viewBox="0 0 25 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="21" height="11" rx="3.5" stroke="#1a1a1a" stroke-opacity="0.35"/>
<rect x="2" y="2" width="16" height="8" rx="2" fill="#1a1a1a"/>
<path d="M23 4v4a2 2 0 000-4z" fill="#1a1a1a" opacity="0.4"/>
</svg>
</div>
</div>
<!-- 导航栏:返回 + 标题"锻炼" -->
<div class="nav-bar">
<!-- 点击返回健康监测首页 -->
<a class="nav-back" onclick="location.href='index.html'">
<svg viewBox="0 0 10 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 1.5L1.5 8.5L8.5 15.5" stroke="#fa8c16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
<span class="nav-title">锻炼</span>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- ① 本周概览卡片 -->
<div class="overview-card">
<div class="card-title">本周概览</div>
<div class="overview-stats">
<!-- 锻炼次数 -->
<div class="stat-item">
<div class="stat-value">3<span class="stat-unit"></span></div>
<div class="stat-label">锻炼次数</div>
</div>
<!-- 累计时长 -->
<div class="stat-item">
<div class="stat-value">2h<span class="stat-unit">15min</span></div>
<div class="stat-label">累计时长</div>
</div>
<!-- 消耗热量 -->
<div class="stat-item">
<div class="stat-value">680<span class="stat-unit">kcal</span></div>
<div class="stat-label">消耗热量</div>
</div>
<!-- 运动距离 -->
<div class="stat-item">
<div class="stat-value">4.2<span class="stat-unit">km</span></div>
<div class="stat-label">运动距离</div>
</div>
</div>
</div>
<!-- ② 时间维度 Tab:周 / 月 切换 -->
<div class="tab-bar">
<!-- 默认选中"周" -->
<div class="tab-item active" onclick="switchTab(this, 'week')"></div>
<div class="tab-item" onclick="switchTab(this, 'month')"></div>
</div>
<!-- ③ 本周锻炼记录列表 -->
<div class="section-title">本周锻炼记录</div>
<!-- 记录 a:户外跑步 -->
<div class="exercise-card">
<div class="exercise-header">
<div class="exercise-type">
<div class="exercise-icon">🏃</div>
<span class="exercise-name">户外跑步</span>
</div>
<span class="exercise-datetime">4月28日 18:30</span>
</div>
<!-- 四项指标:时长 / 距离 / 消耗 / 心率 -->
<div class="exercise-metrics">
<div class="metric-item">
<div class="metric-value">45<span class="metric-unit">min</span></div>
<div class="metric-label">时长</div>
</div>
<div class="metric-item">
<div class="metric-value">5.2<span class="metric-unit">km</span></div>
<div class="metric-label">距离</div>
</div>
<div class="metric-item">
<div class="metric-value">320<span class="metric-unit">kcal</span></div>
<div class="metric-label">消耗</div>
</div>
<div class="metric-item">
<!-- 心率用橙色高亮 -->
<div class="metric-value heart-rate">138<span class="metric-unit">bpm</span></div>
<div class="metric-label">平均心率</div>
</div>
</div>
</div>
<!-- 记录 b:骑行 -->
<div class="exercise-card">
<div class="exercise-header">
<div class="exercise-type">
<div class="exercise-icon">🚴</div>
<span class="exercise-name">骑行</span>
</div>
<span class="exercise-datetime">4月26日 07:15</span>
</div>
<div class="exercise-metrics">
<div class="metric-item">
<div class="metric-value">50<span class="metric-unit">min</span></div>
<div class="metric-label">时长</div>
</div>
<div class="metric-item">
<div class="metric-value">12.8<span class="metric-unit">km</span></div>
<div class="metric-label">距离</div>
</div>
<div class="metric-item">
<div class="metric-value">240<span class="metric-unit">kcal</span></div>
<div class="metric-label">消耗</div>
</div>
<div class="metric-item">
<div class="metric-value heart-rate">125<span class="metric-unit">bpm</span></div>
<div class="metric-label">平均心率</div>
</div>
</div>
</div>
<!-- 记录 c:游泳 -->
<div class="exercise-card">
<div class="exercise-header">
<div class="exercise-type">
<div class="exercise-icon">🏊</div>
<span class="exercise-name">游泳</span>
</div>
<span class="exercise-datetime">4月24日 19:00</span>
</div>
<div class="exercise-metrics">
<div class="metric-item">
<div class="metric-value">40<span class="metric-unit">min</span></div>
<div class="metric-label">时长</div>
</div>
<div class="metric-item">
<div class="metric-value">1.2<span class="metric-unit">km</span></div>
<div class="metric-label">距离</div>
</div>
<div class="metric-item">
<div class="metric-value">280<span class="metric-unit">kcal</span></div>
<div class="metric-label">消耗</div>
</div>
<div class="metric-item">
<div class="metric-value heart-rate">132<span class="metric-unit">bpm</span></div>
<div class="metric-label">平均心率</div>
</div>
</div>
</div>
<!-- ④ 锻炼趋势:近4周柱状图 -->
<div class="trend-card">
<div class="card-title">锻炼趋势</div>
<!-- 柱状图:数据 [2次, 4次, 3次, 3次],最大值4次对应100px高度 -->
<div class="bar-chart">
<!-- 第1周:2次,高度 = 2/4 * 80 = 40px -->
<div class="bar-group">
<span class="bar-value-label">2</span>
<div class="bar" style="height: 40px;"></div>
</div>
<!-- 第2周:4次,高度 = 4/4 * 80 = 80px -->
<div class="bar-group">
<span class="bar-value-label">4</span>
<div class="bar" style="height: 80px;"></div>
</div>
<!-- 第3周:3次,高度 = 3/4 * 80 = 60px -->
<div class="bar-group">
<span class="bar-value-label">3</span>
<div class="bar" style="height: 60px;"></div>
</div>
<!-- 第4周(本周):3次,高亮橙色 -->
<div class="bar-group">
<span class="bar-value-label">3</span>
<div class="bar current" style="height: 60px;"></div>
</div>
</div>
<!-- X轴标签 -->
<div class="bar-x-labels">
<span class="bar-x-label">第1周</span>
<span class="bar-x-label">第2周</span>
<span class="bar-x-label">第3周</span>
<span class="bar-x-label current">本周</span>
</div>
</div>
<!-- ⑤ 统计摘要:本月累计 / 总时长 / 总消耗 -->
<div class="section-title">本月统计</div>
<div class="summary-grid">
<!-- 本月累计次数 -->
<div class="summary-card">
<div class="summary-icon">🏅</div>
<div class="summary-value">12</div>
<div class="summary-sub"></div>
<div class="summary-label">本月累计</div>
</div>
<!-- 总时长 -->
<div class="summary-card">
<div class="summary-icon">⏱️</div>
<div class="summary-value">8h</div>
<div class="summary-sub">30min</div>
<div class="summary-label">总时长</div>
</div>
<!-- 总消耗 -->
<div class="summary-card">
<div class="summary-icon">🔥</div>
<div class="summary-value">2,450</div>
<div class="summary-sub">kcal</div>
<div class="summary-label">总消耗</div>
</div>
</div>
<!-- 底部安全区 -->
<div class="bottom-safe"></div>
</div><!-- /scroll-content -->
</div><!-- /phone-shell -->
<script>
/**
* Tab 切换:周 / 月
* 切换激活样式,实际数据切换可在此扩展
*/
function switchTab(el, period) {
// 移除所有 tab 的激活状态
document.querySelectorAll('.tab-item').forEach(function(item) {
item.classList.remove('active');
});
// 激活当前点击的 tab
el.classList.add('active');
// TODO: 根据 period 切换对应数据('week' | 'month'
}
</script>
</body>
</html>
@@ -0,0 +1,390 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>心率预警历史 - 健康监测 | 健康长庆原型</title>
<style>
/* 全局重置 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* 手机外壳 */
.phone-shell {
width: 390px;
height: 844px;
background: #f5f7fa;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* 状态栏 */
.status-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
background: #fff;
}
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
padding: 0 16px;
flex-shrink: 0;
border-bottom: 1px solid #f0f0f0;
}
.nav-back {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-right: 28px;
}
/* 滚动内容区 */
.scroll-content {
flex: 1;
overflow-y: auto;
background: #f5f7fa;
}
.scroll-content::-webkit-scrollbar { display: none; }
/* 预警统计卡片 */
.stats-card {
margin: 12px 12px 0;
background: #fff;
border-radius: 14px;
padding: 20px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.stats-card__left { display: flex; flex-direction: column; align-items: flex-start; }
.stats-card__label { font-size: 13px; color: #999; margin-bottom: 6px; }
.stats-card__count-row { display: flex; align-items: baseline; gap: 4px; }
.stats-card__count { font-size: 40px; font-weight: 800; color: #ff4d4f; line-height: 1; }
.stats-card__unit { font-size: 14px; color: #ff4d4f; font-weight: 600; }
.stats-card__right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.stats-card__recent-label { font-size: 12px; color: #999; }
.stats-card__recent-time { font-size: 13px; font-weight: 600; color: #333; }
/* 预警图标装饰 */
.stats-card__icon {
width: 48px;
height: 48px;
background: #fff1f0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
/* 列表区块标题 */
.section-title {
font-size: 13px;
color: #999;
padding: 14px 16px 8px;
font-weight: 500;
}
/* 预警记录卡片 */
.alert-card {
margin: 0 12px 10px;
background: #fff;
border-radius: 14px;
padding: 14px 14px 14px 12px;
display: flex;
gap: 12px;
align-items: flex-start;
}
/* 左侧等级圆点 */
.alert-card__dot-col {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3px;
gap: 4px;
}
.alert-card__dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.alert-card__dot--critical { background: #ff4d4f; box-shadow: 0 0 0 3px rgba(255,77,79,0.18); }
.alert-card__dot--warning { background: #faad14; box-shadow: 0 0 0 3px rgba(250,173,20,0.18); }
/* 卡片主体内容 */
.alert-card__body { flex: 1; min-width: 0; }
.alert-card__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.alert-card__time { font-size: 13px; color: #666; }
.alert-card__badge {
font-size: 11px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
flex-shrink: 0;
}
.alert-card__badge--critical { background: #fff1f0; color: #ff4d4f; }
.alert-card__badge--warning { background: #fffbe6; color: #d48806; }
/* 监测值 */
.alert-card__value-row {
display: flex;
align-items: baseline;
gap: 4px;
margin-bottom: 6px;
}
.alert-card__value { font-size: 24px; font-weight: 800; color: #1a1a1a; line-height: 1; }
.alert-card__value-unit { font-size: 13px; color: #999; }
/* 详情行 */
.alert-card__detail-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.alert-card__detail-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #999;
}
.alert-card__detail-item svg { flex-shrink: 0; }
/* 触发原因 */
.alert-card__reason {
margin-top: 8px;
font-size: 12px;
color: #bbb;
background: #fafafa;
border-radius: 6px;
padding: 6px 8px;
line-height: 1.5;
}
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<!-- WiFi 图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
<circle cx="12" cy="20" r="1" fill="#333" stroke="none"/>
</svg>
<!-- 电池图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<rect x="1" y="6" width="18" height="12" rx="2"/>
<path d="M21 10v4"/>
<rect x="3" y="8" width="12" height="8" rx="1" fill="#333" stroke="none"/>
</svg>
</div>
</div>
<!-- 导航栏:返回心率详情页 -->
<div class="nav-bar">
<div class="nav-back" onclick="location.href='heart-rate.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round">
<path d="M15 19l-7-7 7-7"/>
</svg>
</div>
<div class="nav-title">心率预警历史</div>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">3</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 预警心形图标 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" fill="#ff4d4f" stroke="#ff4d4f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-28 16:32</div>
</div>
</div>
<!-- 预警记录列表标题 -->
<div class="section-title">预警记录(共 3 条)</div>
<!-- 预警记录 1:严重 - 2026-04-28 -->
<div class="alert-card">
<!-- 左侧严重等级红点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-28 16:32</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">142</span>
<span class="alert-card__value-unit">bpm</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 5 分钟</span>
</div>
<!-- 心率标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 140 bpm 阈值</div>
</div>
</div>
<!-- 预警记录 2:警告 - 2026-04-25 -->
<div class="alert-card">
<!-- 左侧警告等级黄点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-25 14:18</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">128</span>
<span class="alert-card__value-unit">bpm</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 3 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 120 bpm 阈值</div>
</div>
</div>
<!-- 预警记录 3:警告 - 2026-04-20 -->
<div class="alert-card">
<!-- 左侧警告等级黄点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-20 09:45</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">126</span>
<span class="alert-card__value-unit">bpm</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 2 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 120 bpm 阈值</div>
</div>
</div>
<!-- 底部安全区 -->
<div class="bottom-safe"></div>
</div><!-- /scroll-content -->
</div><!-- /phone-shell -->
</body>
</html>
+316
View File
@@ -0,0 +1,316 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>心率详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 主题色(心率红) */
:root { --theme: #ff7a8a; --theme2:#ffb3bd; }
.top-panel { background: #fff; padding-top: 2px; }
.time-tabs { display: flex; justify-content: center; gap: 42px; padding: 2px 0 8px; }
.time-tab { position: relative; font-size: 18px; color: #444; cursor: pointer; padding: 0 2px 6px; user-select: none; }
.time-tab.active { color: var(--theme); }
.time-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; margin: auto; width: 22px; height: 3px; border-radius: 2px; background: var(--theme); }
.summary-area { text-align: center; padding: 12px 24px 6px; }
.date-row { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; color: #666; font-weight: 500; }
.range-text { margin-top: 4px; font-size: 16px; color: #666; }
.main-row { margin-top: 2px; display: inline-flex; align-items: baseline; gap: 4px; }
.main-row__value { font-size: 48px; line-height: 1; font-weight: 500; color: #1f1f1f; letter-spacing: -1px; }
.main-row__unit { font-size: 18px; color: #333; }
.main-badge { display: inline-flex; align-items: center; justify-content: center; margin-top: 6px; padding: 3px 12px; border-radius: 18px; font-size: 12px; font-weight: 600; background: #f6ffed; color: #52c41a; }
.chart-wrap { position: relative; background: #fff; padding: 6px 16px 0; }
.chart-box { position: relative; height: 160px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: var(--theme); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: var(--theme); }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 0; height: 24px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 10px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: -4px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.card { margin: 8px 12px 0; background: #fff; border-radius: 22px; padding: 14px 12px; display: flex; gap: 0; }
.metric-row { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 6px; }
.metric-left__label { font-size: 12px; color: #777; margin-bottom: 4px; }
.metric-left__value { display: flex; align-items: baseline; gap: 2px; }
.metric-left__num { font-size: 16px; font-weight: 600; color: #222; }
.metric-left__unit { font-size: 12px; color: #777; }
.metric-right { font-size: 12px; color: #777; text-align: center; }
.metric-split { width: 1px; background: #ececec; margin: 0; }
.alert-btn { margin: 8px 12px 0; border: 1.5px solid var(--theme); border-radius: 12px; padding: 11px; text-align: center; font-size: 13px; color: var(--theme); font-weight: 600; cursor: pointer; background: #fff; display: flex; align-items: center; justify-content: center; gap: 6px; }
.foot-note { padding: 10px 0 12px; text-align: center; font-size: 11px; color: #b3b3b3; }
.bottom-safe { height: 12px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">心率</div>
</div>
<div class="scroll-content">
<div class="top-panel">
<div class="time-tabs">
<div class="time-tab active" data-mode="day"></div>
<div class="time-tab" data-mode="week"></div>
<div class="time-tab" data-mode="month"></div>
<div class="time-tab" data-mode="year"></div>
</div>
<div class="summary-area">
<div class="date-row">
<span id="dateText">4月30日 周四</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="#666"><path d="M7 10l5 5 5-5z"/></svg>
</div>
<div class="range-text" id="rangeText">00:00-23:59</div>
<div class="main-row">
<span class="main-row__value" id="mainValue">72</span>
<span class="main-row__unit" id="mainUnit">bpm</span>
</div>
<div class="main-badge" id="statusBadge">正常</div>
</div>
<div class="chart-wrap">
<div class="chart-box">
<svg class="chart-svg" id="chart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="line" class="chart-path"></path>
<circle id="dot" class="chart-dot" r="3.2"></circle>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
</div>
</div>
</div>
<div class="card">
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最高</div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">126</span><span class="metric-left__unit">bpm</span></div>
</div>
<div class="metric-right" id="maxHint">14:20</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最低</div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">68</span><span class="metric-left__unit">bpm</span></div>
</div>
<div class="metric-right" id="minHint">03:40</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">平均</div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">82</span><span class="metric-left__unit">bpm</span></div>
</div>
<div class="metric-right" id="avgHint">全天</div>
</div>
</div>
<div class="alert-btn" onclick="location.href='heart-rate-alerts.html'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--theme)" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
<div class="foot-note">以上数据为原型示例,实际以设备同步为准</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
unit: 'bpm',
decimals: 0,
points: [84,82,80,78,76,74,72,70,68,75,79,83,88,96,102,126,108,94,90,87,85,84,83,72],
totalMode: 'last',
yTicks: [140, 110, 80, 60, 40],
xLabels: ['00:00','06:00','12:00','18:00','24:00'],
status: '正常',
resting: 62,
max: { v:126, hint:'14:20' },
min: { v:68, hint:'03:40' },
avg: { v:82, hint:'全天' }
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: 'bpm',
decimals: 0,
points: [78, 80, 82, 79, 83, 81, 80],
totalMode: 'avg',
yTicks: [120, 100, 80, 60, 40],
xLabels: ['周一','周二','周三','周四','周五','周六','周日'],
status: '正常',
resting: 63,
max: { v:118, hint:'周三 18:10' },
min: { v:61, hint:'周二 03:50' },
avg: { v:80, hint:'本周' }
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: 'bpm',
decimals: 0,
points: [79,81,80,82,83,78,80,81,82,79,80,81,82,83,79,80,81,82,80,79,81,82,80,79,81,82,80,79,81,80],
totalMode: 'avg',
yTicks: [120, 100, 80, 60, 40],
xLabels: ['1日','7日','14日','21日','30日'],
status: '正常',
resting: 64,
max: { v:126, hint:'04-15 14:20' },
min: { v:58, hint:'04-08 03:40' },
avg: { v:80, hint:'本月' }
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: 'bpm',
decimals: 0,
points: [79,80,81,80,79,78,79,80,81,80,79,80],
totalMode: 'avg',
yTicks: [120, 100, 80, 60, 40],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
status: '正常',
resting: 64,
max: { v:132, hint:'03月' },
min: { v:57, hint:'02月' },
avg: { v:80, hint:'全年' }
}
};
function avg(arr) {
if (!arr.length) return 0;
var sum = arr.reduce(function(s, v){ return s + v; }, 0);
return sum / arr.length;
}
function getValue(cfg) {
if (cfg.totalMode === 'last') return cfg.points[cfg.points.length - 1];
if (cfg.totalMode === 'avg') return avg(cfg.points);
if (cfg.totalMode === 'max') return Math.max.apply(null, cfg.points);
if (cfg.totalMode === 'min') return Math.min.apply(null, cfg.points);
return cfg.points[cfg.points.length - 1];
}
function formatValue(val, decimals) {
if (typeof val !== 'number') return String(val);
if (decimals != null) return val.toFixed(decimals);
return String(val);
}
function renderChart(cfg) {
var chartWidth = 330;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('line');
var dotEl = document.getElementById('dot');
grid.innerHTML = '';
cfg.yTicks.forEach(function(_, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(tick, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatValue(tick, 0) + '</span>';
}).join('');
var pts = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var d = pts.map(function(p, index) {
return (index === 0 ? 'M' : 'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = pts[pts.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
xAxis.innerHTML = cfg.xLabels.map(function(label) {
return '<span>' + label + '</span>';
}).join('');
}
function renderMode(mode) {
var cfg = dataMap[mode];
var value = getValue(cfg);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, 0);
document.getElementById('maxHint').textContent = cfg.max.hint;
document.getElementById('minValue').textContent = formatValue(cfg.min.v, 0);
document.getElementById('minHint').textContent = cfg.min.hint;
document.getElementById('avgValue').textContent = formatValue(cfg.avg.v, 0);
document.getElementById('avgHint').textContent = cfg.avg.hint;
renderChart(cfg);
}
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
};
});
renderMode('day');
</script>
</body>
</html>
+278
View File
@@ -0,0 +1,278 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康监测 - 员工端 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 设备状态 */
.device-bar { margin: 12px 12px 0; background: linear-gradient(135deg, #13c2c2 0%, #0d9b9b 100%); border-radius: 12px; padding: 14px 16px; display: flex; align-items: center; gap: 12px; color: #fff; }
.device-icon { width: 44px; height: 44px; background: rgba(255,255,255,0.2); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 24px; flex-shrink: 0; }
.device-info { flex: 1; }
.device-name { font-size: 15px; font-weight: 600; }
.device-meta { font-size: 12px; opacity: 0.85; margin-top: 2px; }
.device-battery { display: flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; }
.battery-icon { width: 22px; height: 12px; border: 1.5px solid #fff; border-radius: 2px; position: relative; }
.battery-icon::after { content: ''; position: absolute; right: -4px; top: 2.5px; width: 2px; height: 5px; background: #fff; border-radius: 0 1px 1px 0; }
.battery-fill { height: 100%; background: #fff; border-radius: 1px; }
/* 监测卡片网格 */
.monitor-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 12px; }
.monitor-card { background: #fff; border-radius: 12px; padding: 16px; cursor: pointer; position: relative; transition: transform 0.15s; }
.monitor-card:active { transform: scale(0.97); }
.monitor-card--full { grid-column: 1 / -1; }
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.card-header__left { display: flex; align-items: center; min-width: 0; flex: 1; }
.card-header__right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.card-icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
.card-label { font-size: 13px; color: #666; margin-left: 8px; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-meta { font-size: 12px; color: #999; }
.card-alert-badge { width: 18px; height: 18px; background: #ff4d4f; border-radius: 50%; font-size: 10px; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; }
.card-arrow { width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; }
.card-arrow svg { width: 16px; height: 16px; display: block; }
.card-sync { font-size: 10px; color: #bbb; margin-top: 8px; }
.card-value { font-size: 28px; font-weight: 800; color: #1a1a1a; line-height: 1.1; }
.card-unit { font-size: 13px; color: #999; font-weight: 400; margin-left: 2px; }
.card-status { display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 4px; margin-top: 6px; }
.card-status--normal { color: #52c41a; background: #f6ffed; }
.card-status--warn { color: #faad14; background: #fffbe6; }
.card-status--danger { color: #ff4d4f; background: #fff2f0; }
.card-sub { font-size: 11px; color: #999; margin-top: 6px; }
.card-progress { margin-top: 8px; }
.card-progress__bar { height: 6px; background: #f0f0f0; border-radius: 3px; overflow: hidden; }
.card-progress__fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
.card-progress__text { font-size: 11px; color: #999; margin-top: 4px; }
/* 睡眠分段 */
.sleep-segments { display: flex; gap: 4px; margin-top: 8px; height: 6px; border-radius: 3px; overflow: hidden; }
.sleep-seg { height: 100%; border-radius: 3px; }
.sleep-seg--deep { background: #1890ff; }
.sleep-seg--light { background: #91d5ff; }
.sleep-seg--rem { background: #b37feb; }
.sleep-seg--awake { background: #ffd591; }
.sleep-legend { display: flex; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.sleep-legend__item { display: flex; align-items: center; gap: 3px; font-size: 10px; color: #999; }
.sleep-legend__dot { width: 6px; height: 6px; border-radius: 50%; }
/* 锻炼卡片 */
.exercise-stats { display: flex; gap: 12px; margin-top: 8px; }
.exercise-stat { flex: 1; text-align: center; background: #f5f7fa; border-radius: 8px; padding: 8px 4px; }
.exercise-stat__val { font-size: 16px; font-weight: 700; color: #1a1a1a; }
.exercise-stat__label { font-size: 10px; color: #999; margin-top: 2px; }
/* 预警入口 */
.card-alert-link { display: flex; align-items: center; justify-content: center; gap: 4px; font-size: 11px; color: #ff4d4f; margin-top: 8px; padding-top: 8px; border-top: 1px solid #f5f5f5; cursor: pointer; }
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="history.back()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">健康监测</div>
</div>
<div class="scroll-content">
<!-- 设备连接状态 -->
<div class="device-bar">
<div class="device-icon"></div>
<div class="device-info">
<div class="device-name">华为 WATCH GT5 Pro</div>
<div class="device-meta">SF55996622586 · 已连接</div>
</div>
<!-- <div class="device-battery">-->
<!-- <div class="battery-icon"><div class="battery-fill" style="width:72%"></div></div>-->
<!-- 72%-->
<!-- </div>-->
</div>
<!-- 监测卡片网格 -->
<div class="monitor-grid">
<!-- 步数 -->
<div class="monitor-card" onclick="location.href='steps.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#e6f7ff">🏃</div>
<span class="card-label">步数</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">8,562<span class="card-unit"></span></div>
<div class="card-progress">
<div class="card-progress__text">相当于步行5.2公里</div>
</div>
<div class="card-sync">同步至 04-30 09:38</div>
</div>
<!-- 睡眠 -->
<div class="monitor-card" onclick="location.href='sleep.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#f0e6ff">🌙</div>
<span class="card-label">睡眠</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">7<span class="card-unit">h</span>32<span class="card-unit">min</span></div>
<div class="card-progress__text">平均睡眠8小时57分</div>
<div class="card-sync">同步至 04-30 07:05</div>
<!-- <div class="sleep-segments">-->
<!-- <div class="sleep-seg sleep-seg&#45;&#45;deep" style="width:28%"></div>-->
<!-- <div class="sleep-seg sleep-seg&#45;&#45;light" style="width:42%"></div>-->
<!-- <div class="sleep-seg sleep-seg&#45;&#45;rem" style="width:20%"></div>-->
<!-- <div class="sleep-seg sleep-seg&#45;&#45;awake" style="width:10%"></div>-->
<!-- </div>-->
<!-- <div class="sleep-legend">-->
<!-- <div class="sleep-legend__item"><div class="sleep-legend__dot" style="background:#1890ff"></div>深睡</div>-->
<!-- <div class="sleep-legend__item"><div class="sleep-legend__dot" style="background:#91d5ff"></div>浅睡</div>-->
<!-- <div class="sleep-legend__item"><div class="sleep-legend__dot" style="background:#b37feb"></div>REM</div>-->
<!-- <div class="sleep-legend__item"><div class="sleep-legend__dot" style="background:#ffd591"></div>清醒</div>-->
<!-- </div>-->
</div>
<!-- 心率 -->
<div class="monitor-card" onclick="location.href='heart-rate.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#fff1f0">❤️</div>
<span class="card-label">心率</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">72<span class="card-unit">bpm</span></div>
<div class="card-sub">范围:89~120</div>
<div class="card-sync">同步至 04-30 09:37</div>
<div class="card-alert-link" onclick="event.stopPropagation();location.href='heart-rate-alerts.html'">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#ff4d4f" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
</div>
<!-- 体温 -->
<div class="monitor-card" onclick="location.href='temperature.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#f9f0ff">🌡️</div>
<span class="card-label">体温</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">36.5<span class="card-unit">°C</span></div>
<div class="card-sub">范围 36.2~37.1°C</div>
<div class="card-sync">同步至 04-30 09:30</div>
<div class="card-alert-link" onclick="event.stopPropagation();location.href='temperature-alerts.html'">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#ff4d4f" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
</div>
<!-- 血氧 -->
<div class="monitor-card" onclick="location.href='blood-oxygen.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#f6ffed">💧</div>
<span class="card-label">血氧</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">98<span class="card-unit">%</span></div>
<div class="card-sub">范围 95~99%</div>
<div class="card-sync">同步至 04-30 09:35</div>
<div class="card-alert-link" onclick="event.stopPropagation();location.href='blood-oxygen-alerts.html'">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#ff4d4f" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
</div>
<!-- 压力 -->
<div class="monitor-card" onclick="location.href='stress.html'">
<div class="card-header">
<div class="card-header__left">
<div class="card-icon" style="background:#fffbe6">🧠</div>
<span class="card-label">压力</span>
</div>
<div class="card-header__right">
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</div>
</div>
</div>
<div class="card-value">42</div>
<div class="card-sub">范围 18~82</div>
<div class="card-sync">同步至 04-30 09:36</div>
<div class="card-alert-link" onclick="event.stopPropagation();location.href='stress-alerts.html'">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#ff4d4f" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
</div>
<!-- 锻炼 -->
<!-- <div class="monitor-card monitor-card&#45;&#45;full" onclick="location.href='exercise.html'">-->
<!-- <div class="card-header">-->
<!-- <div class="card-header__left">-->
<!-- <div class="card-icon" style="background:#fff7e6">🏋️</div>-->
<!-- <span class="card-label">锻炼</span>-->
<!-- </div>-->
<!-- <div class="card-header__right">-->
<!-- <span class="card-meta">本周</span>-->
<!-- <div class="card-arrow">-->
<!-- <svg viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="exercise-stats">-->
<!-- <div class="exercise-stat">-->
<!-- <div class="exercise-stat__val">3<span style="font-size:12px;color:#999;font-weight:400"> 次</span></div>-->
<!-- <div class="exercise-stat__label">锻炼次数</div>-->
<!-- </div>-->
<!-- <div class="exercise-stat">-->
<!-- <div class="exercise-stat__val">2h15<span style="font-size:12px;color:#999;font-weight:400"> min</span></div>-->
<!-- <div class="exercise-stat__label">累计时长</div>-->
<!-- </div>-->
<!-- <div class="exercise-stat">-->
<!-- <div class="exercise-stat__val">680<span style="font-size:12px;color:#999;font-weight:400"> kcal</span></div>-->
<!-- <div class="exercise-stat__label">消耗热量</div>-->
<!-- </div>-->
<!-- <div class="exercise-stat">-->
<!-- <div class="exercise-stat__val">4.2<span style="font-size:12px;color:#999;font-weight:400"> km</span></div>-->
<!-- <div class="exercise-stat__label">运动距离</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="card-sync">同步至 04-30 09:20</div>-->
<!-- </div>-->
</div>
</div>
<div class="bottom-safe"></div>
</div>
</body>
</html>
+309
View File
@@ -0,0 +1,309 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>睡眠详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
/* 状态栏 */
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
/* 滚动内容区 */
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 核心数据区 */
.hero { background: #fff; padding: 24px 16px 20px; text-align: center; }
/* 评分圆环 */
.hero__ring { width: 160px; height: 160px; margin: 0 auto 12px; position: relative; }
.hero__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.hero__ring-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); text-align: center; }
.hero__score { font-size: 38px; font-weight: 800; color: #1a1a1a; line-height: 1; }
.hero__score-label { font-size: 12px; color: #999; margin-top: 2px; }
/* 睡眠时长 */
.hero__duration { font-size: 22px; font-weight: 700; color: #1a1a1a; margin-bottom: 4px; }
.hero__duration span { font-size: 14px; font-weight: 400; color: #666; }
/* 入睡/起床时间 */
.hero-times { display: flex; justify-content: center; gap: 40px; margin-top: 14px; }
.hero-time { text-align: center; }
.hero-time__val { font-size: 20px; font-weight: 700; color: #1a1a1a; }
.hero-time__label { font-size: 11px; color: #999; margin-top: 2px; }
.hero-time__icon { font-size: 16px; margin-bottom: 2px; }
/* 分隔线 */
.divider { width: 1px; height: 40px; background: #f0f0f0; align-self: center; }
/* 睡眠分段条形图区 */
.section { margin: 10px 12px 0; background: #fff; border-radius: 12px; padding: 16px; }
.section-title { font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 14px; }
/* 横向分段条 */
.stage-bar-wrap { display: flex; border-radius: 6px; overflow: hidden; height: 14px; margin-bottom: 14px; }
.stage-bar-seg { height: 100%; transition: width 0.4s; }
/* 图例列表 */
.stage-legend { display: flex; flex-direction: column; gap: 8px; }
.stage-item { display: flex; align-items: center; gap: 8px; }
.stage-dot { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }
.stage-name { font-size: 12px; color: #666; width: 36px; }
.stage-bar-bg { flex: 1; height: 6px; background: #f5f7fa; border-radius: 3px; overflow: hidden; }
.stage-bar-fill { height: 100%; border-radius: 3px; }
.stage-pct { font-size: 12px; color: #999; width: 30px; text-align: right; }
.stage-dur { font-size: 12px; font-weight: 600; color: #1a1a1a; width: 56px; text-align: right; }
/* 时间维度Tab */
.time-tabs { display: flex; margin: 10px 12px 0; background: #fff; border-radius: 10px; overflow: hidden; }
.time-tab { flex: 1; text-align: center; padding: 10px 0; font-size: 13px; color: #666; cursor: pointer; }
.time-tab.active { background: #1890ff; color: #fff; border-radius: 10px; font-weight: 600; }
/* 睡眠深度曲线 */
.depth-chart { width: 100%; height: 80px; }
.depth-axis { display: flex; justify-content: space-between; font-size: 10px; color: #bbb; margin-top: 4px; }
/* 近7天趋势 */
.trend-chart { width: 100%; height: 100px; }
.trend-axis { display: flex; justify-content: space-between; font-size: 10px; color: #bbb; margin-top: 4px; }
/* 统计摘要 */
.summary { display: flex; gap: 8px; margin: 10px 12px 0; }
.summary-card { flex: 1; background: #fff; border-radius: 12px; padding: 14px 8px; text-align: center; }
.summary-card__val { font-size: 18px; font-weight: 700; color: #1a1a1a; line-height: 1.2; }
.summary-card__unit { font-size: 11px; color: #999; }
.summary-card__label { font-size: 11px; color: #999; margin-top: 4px; }
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<!-- WiFi图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<!-- 电池图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<!-- 导航栏 -->
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">睡眠</div>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 核心数据区:评分圆环 + 时长 + 入睡/起床 -->
<div class="hero">
<!-- 睡眠评分圆环 -->
<div class="hero__ring">
<svg viewBox="0 0 160 160">
<defs>
<linearGradient id="sleepGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1890ff"/>
<stop offset="100%" stop-color="#b37feb"/>
</linearGradient>
</defs>
<!-- 背景圆环 -->
<circle cx="80" cy="80" r="70" fill="none" stroke="#f0f0f0" stroke-width="10"/>
<!-- 评分圆弧:85分 = 85% 周长,周长=2π×70≈439.885%≈373.8,偏移=439.8-373.8=66 -->
<circle cx="80" cy="80" r="70" fill="none" stroke="url(#sleepGrad)" stroke-width="10" stroke-linecap="round" stroke-dasharray="439.8" stroke-dashoffset="66"/>
</svg>
<div class="hero__ring-center">
<div class="hero__score">85</div>
<div class="hero__score-label">睡眠评分</div>
</div>
</div>
<!-- 睡眠时长 -->
<div class="hero__duration">7<span>h</span> 32<span>min</span></div>
<!-- 入睡 / 起床时间 -->
<div class="hero-times">
<div class="hero-time">
<div class="hero-time__icon">🌙</div>
<div class="hero-time__val">23:12</div>
<div class="hero-time__label">入睡</div>
</div>
<div class="divider"></div>
<div class="hero-time">
<div class="hero-time__icon">☀️</div>
<div class="hero-time__val">06:44</div>
<div class="hero-time__label">起床</div>
</div>
</div>
</div>
<!-- 睡眠分段条形图 -->
<div class="section">
<div class="section-title">睡眠阶段</div>
<!-- 横向分段总条 -->
<div class="stage-bar-wrap">
<!-- 深睡 28% -->
<div class="stage-bar-seg" style="width:28%;background:#1890ff;"></div>
<!-- 浅睡 42% -->
<div class="stage-bar-seg" style="width:42%;background:#91d5ff;"></div>
<!-- REM 20% -->
<div class="stage-bar-seg" style="width:20%;background:#b37feb;"></div>
<!-- 清醒 10% -->
<div class="stage-bar-seg" style="width:10%;background:#ffd591;"></div>
</div>
<!-- 各阶段图例 + 进度条 -->
<div class="stage-legend">
<!-- 深睡 -->
<div class="stage-item">
<div class="stage-dot" style="background:#1890ff;"></div>
<div class="stage-name">深睡</div>
<div class="stage-bar-bg"><div class="stage-bar-fill" style="width:28%;background:#1890ff;"></div></div>
<div class="stage-pct">28%</div>
<div class="stage-dur">2h 06min</div>
</div>
<!-- 浅睡 -->
<div class="stage-item">
<div class="stage-dot" style="background:#91d5ff;"></div>
<div class="stage-name">浅睡</div>
<div class="stage-bar-bg"><div class="stage-bar-fill" style="width:42%;background:#91d5ff;"></div></div>
<div class="stage-pct">42%</div>
<div class="stage-dur">3h 10min</div>
</div>
<!-- REM -->
<div class="stage-item">
<div class="stage-dot" style="background:#b37feb;"></div>
<div class="stage-name">REM</div>
<div class="stage-bar-bg"><div class="stage-bar-fill" style="width:20%;background:#b37feb;"></div></div>
<div class="stage-pct">20%</div>
<div class="stage-dur">1h 30min</div>
</div>
<!-- 清醒 -->
<div class="stage-item">
<div class="stage-dot" style="background:#ffd591;"></div>
<div class="stage-name">清醒</div>
<div class="stage-bar-bg"><div class="stage-bar-fill" style="width:10%;background:#ffd591;"></div></div>
<div class="stage-pct">10%</div>
<div class="stage-dur">46min</div>
</div>
</div>
</div>
<!-- 时间维度Tab -->
<div class="time-tabs">
<div class="time-tab active"></div>
<div class="time-tab"></div>
<div class="time-tab"></div>
</div>
<!-- 24h睡眠深度曲线 -->
<div class="section">
<div class="section-title">睡眠深度曲线</div>
<svg class="depth-chart" id="depthChart" viewBox="0 0 320 80" preserveAspectRatio="none"></svg>
<div class="depth-axis"><span>22:00</span><span>00:00</span><span>02:00</span><span>04:00</span><span>06:00</span><span>07:00</span></div>
</div>
<!-- 近7天趋势 -->
<div class="section">
<div class="section-title">近 7 天趋势</div>
<svg class="trend-chart" id="trendChart" viewBox="0 0 320 100" preserveAspectRatio="none"></svg>
<div class="trend-axis"><span>4/24</span><span>4/25</span><span>4/26</span><span>4/27</span><span>4/28</span><span>4/29</span><span>4/30</span></div>
</div>
<!-- 统计摘要 -->
<div class="summary">
<div class="summary-card">
<div class="summary-card__val">7h<br>20min</div>
<div class="summary-card__label">7日平均</div>
</div>
<div class="summary-card">
<div class="summary-card__val">8h<br>06min</div>
<div class="summary-card__label">最长睡眠</div>
</div>
<div class="summary-card">
<div class="summary-card__val">82</div>
<div class="summary-card__unit"></div>
<div class="summary-card__label">睡眠规律性</div>
</div>
</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
/* ---- 24h睡眠深度曲线(SVG路径,模拟真实睡眠波形) ---- */
(function() {
/* 睡眠深度数据:0=清醒, 1=浅睡, 2=深睡, 3=REM,共48个采样点(每15分钟一个) */
/* 时间范围:22:00 ~ 07:00,共9小时 = 36个15分钟 */
var depthData = [
0, 0, 1, 1, 2, 2, 2, 3, 3, 2, /* 22:00-24:30 入睡过渡 */
2, 2, 3, 3, 1, 1, 2, 2, 2, 3, /* 00:30-03:00 深睡阶段 */
3, 2, 1, 1, 0, 1, 2, 2, 3, 3, /* 03:00-05:30 REM循环 */
2, 1, 1, 0, 0, 0 /* 05:30-07:00 浅睡至清醒 */
];
var n = depthData.length;
var w = 320, h = 80;
var padT = 8, padB = 8;
var chartH = h - padT - padB;
/* 深度值映射:0=清醒(顶部), 3=深睡(底部) */
function yOf(d) { return padT + (d / 3) * chartH; }
/* 生成平滑路径点 */
var pts = depthData.map(function(d, i) {
return { x: (i / (n - 1)) * w, y: yOf(d) };
});
/* 构建平滑曲线(使用三次贝塞尔近似) */
var path = 'M ' + pts[0].x.toFixed(1) + ',' + pts[0].y.toFixed(1);
for (var i = 1; i < pts.length; i++) {
var prev = pts[i - 1], cur = pts[i];
var cpx = (prev.x + cur.x) / 2;
path += ' C ' + cpx.toFixed(1) + ',' + prev.y.toFixed(1) + ' ' + cpx.toFixed(1) + ',' + cur.y.toFixed(1) + ' ' + cur.x.toFixed(1) + ',' + cur.y.toFixed(1);
}
/* 填充区域路径 */
var areaPath = path + ' L ' + w + ',' + h + ' L 0,' + h + ' Z';
var svg = document.getElementById('depthChart');
svg.innerHTML =
'<defs>' +
'<linearGradient id="depthGrad" x1="0" y1="0" x2="0" y2="1">' +
'<stop offset="0%" stop-color="#1890ff" stop-opacity="0.35"/>' +
'<stop offset="100%" stop-color="#1890ff" stop-opacity="0.04"/>' +
'</linearGradient>' +
'</defs>' +
'<path d="' + areaPath + '" fill="url(#depthGrad)"/>' +
'<path d="' + path + '" fill="none" stroke="#1890ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
})();
/* ---- 近7天睡眠时长趋势折线图 ---- */
(function() {
/* 单位:小时 */
var weekData = [7.2, 6.8, 7.5, 8.1, 6.5, 7.8, 7.5];
var n = weekData.length;
var w = 320, h = 100;
var padT = 10, padB = 10;
var chartH = h - padT - padB;
var wMax = Math.max.apply(null, weekData);
var wMin = Math.min.apply(null, weekData);
var range = wMax - wMin || 1;
/* 计算各点坐标 */
var pts = weekData.map(function(v, i) {
return {
x: (i / (n - 1)) * w,
y: padT + (1 - (v - wMin) / range) * chartH
};
});
var line = pts.map(function(p) { return p.x.toFixed(1) + ',' + p.y.toFixed(1); }).join(' ');
var area = '0,' + h + ' ' + line + ' ' + w + ',' + h;
var svg = document.getElementById('trendChart');
svg.innerHTML =
'<defs>' +
'<linearGradient id="trendGrad" x1="0" y1="0" x2="0" y2="1">' +
'<stop offset="0%" stop-color="#1890ff" stop-opacity="0.3"/>' +
'<stop offset="100%" stop-color="#1890ff" stop-opacity="0.02"/>' +
'</linearGradient>' +
'</defs>' +
'<polygon points="' + area + '" fill="url(#trendGrad)"/>' +
'<polyline points="' + line + '" fill="none" stroke="#1890ff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>';
/* 数据点圆圈 */
pts.forEach(function(p) {
svg.innerHTML += '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="4" fill="#fff" stroke="#1890ff" stroke-width="2"/>';
});
})();
/* ---- 时间维度Tab切换 ---- */
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(t) { t.classList.remove('active'); });
tab.classList.add('active');
};
});
</script>
</body>
</html>
+283
View File
@@ -0,0 +1,283 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>步数详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
.top-panel { background: #fff; padding-top: 4px; }
.time-tabs { display: flex; justify-content: center; gap: 42px; padding: 4px 0 14px; }
.time-tab { position: relative; font-size: 18px; color: #444; cursor: pointer; padding: 0 2px 8px; user-select: none; }
.time-tab.active { color: #68be5a; }
.time-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; margin: auto; width: 22px; height: 3px; border-radius: 2px; background: #68be5a; }
.summary-area { text-align: center; padding: 18px 24px 8px; }
.date-row { display: inline-flex; align-items: center; gap: 6px; font-size: 18px; color: #666; font-weight: 500; }
.range-text { margin-top: 8px; font-size: 18px; color: #666; }
.steps-main { margin-top: 4px; display: inline-flex; align-items: baseline; gap: 4px; }
.steps-main__value { font-size: 58px; line-height: 1; font-weight: 500; color: #1f1f1f; letter-spacing: -1px; }
.steps-main__unit { font-size: 21px; color: #333; }
.chart-wrap { position: relative; background: #fff; padding: 10px 16px 0; }
.chart-box { position: relative; height: 258px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: #74c26b; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: #74c26b; }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 2px 0 0; height: 30px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 14px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: 1px; width: 32px; height: 32px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.card { margin: 12px 12px 0; background: #fff; border-radius: 22px; padding: 20px 18px 18px; }
.card-head { display: flex; align-items: center; justify-content: space-between; }
.card-title { font-size: 16px; font-weight: 600; color: #222; }
.edit-link { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; color: #555; }
.goal-row { margin-top: 18px; display: flex; align-items: baseline; gap: 2px; color: #1f1f1f; }
.goal-row__current { font-size: 22px; font-weight: 600; }
.goal-row__divider, .goal-row__target, .goal-row__unit { font-size: 14px; color: #666; }
.goal-progress { margin-top: 18px; height: 26px; background: #f1f1f1; border-radius: 13px; position: relative; overflow: hidden; }
.goal-progress__fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: 13px; background: linear-gradient(90deg, #58c45c 0%, #86df75 100%); }
.goal-progress__marker { position: absolute; left: 0; top: 50%; width: 18px; height: 18px; border-radius: 50%; transform: translate(-50%, -50%); background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.14); border: 1px solid #ededed; display: flex; align-items: center; justify-content: center; }
.goal-progress__marker::before { content: '🏃'; font-size: 9px; line-height: 1; filter: grayscale(1); opacity: 0.55; }
.motivation-row { margin-top: 22px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.motivation-text { font-size: 15px; color: #333; }
.walk-btn { flex-shrink: 0; min-width: 92px; height: 36px; border-radius: 18px; background: #f4f8ef; color: #7bc86b; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 500; }
.section-divider { height: 1px; background: #ececec; margin: 18px 0 14px; }
.metric-row { display: flex; align-items: flex-end; justify-content: space-between; padding: 10px 0; }
.metric-left__label { font-size: 14px; color: #777; margin-bottom: 6px; }
.metric-left__value { display: flex; align-items: baseline; gap: 4px; }
.metric-left__num { font-size: 18px; font-weight: 600; color: #222; }
.metric-left__unit { font-size: 14px; color: #777; }
.metric-right { font-size: 14px; color: #777; text-align: right; }
.metric-split { height: 1px; background: #ececec; }
.foot-note { padding: 14px 0 20px; text-align: center; font-size: 12px; color: #b3b3b3; }
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">步数</div>
</div>
<div class="scroll-content">
<div class="top-panel">
<div class="time-tabs">
<div class="time-tab active" data-mode="day"></div>
<div class="time-tab" data-mode="week"></div>
<div class="time-tab" data-mode="month"></div>
<div class="time-tab" data-mode="year"></div>
</div>
<div class="summary-area">
<div class="date-row">
<span id="dateText">4月30日 周四</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="#666"><path d="M7 10l5 5 5-5z"/></svg>
</div>
<div class="range-text" id="rangeText">00:00-23:59</div>
<div class="steps-main">
<span class="steps-main__value" id="totalSteps">1,869</span>
<span class="steps-main__unit"></span>
</div>
</div>
<div class="chart-wrap">
<div class="chart-box">
<svg class="chart-svg" id="stepChart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="stepLine" class="chart-path"></path>
<circle id="stepDot" class="chart-dot" r="3.2"></circle>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
</div>
</div>
</div>
<div class="card">
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">消耗</div>
<div class="metric-left__value">
<span class="metric-left__num" id="calorieValue">55</span>
<span class="metric-left__unit">千卡</span>
</div>
</div>
<div class="metric-right" id="calorieCompare">相当于消耗 1 个鸡蛋</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">距离</div>
<div class="metric-left__value">
<span class="metric-left__num" id="distanceValue">1.33</span>
<span class="metric-left__unit">公里</span>
</div>
</div>
<div class="metric-right" id="distanceCompare">相当于走了 3.34 圈标准操场</div>
</div>
</div>
<div class="foot-note">以上数据由步数换算而成</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
goal: 10000,
points: [0,0,0,0,0,0,0,30,60,80,90,110,130,180,240,320,420,560,720,960,1210,1480,1680,1869],
totalMode: 'last',
yTicks: [12000, 9000, 6000, 3000, 0],
xLabels: ['00:00','06:00','12:00','18:00','24:00']
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
goal: 70000,
points: [8320, 9640, 10280, 1869, 7560, 5410, 5660],
totalMode: 'sum',
yTicks: [12000, 9000, 6000, 3000, 0],
xLabels: ['周一','周二','周三','周四','周五','周六','周日']
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
goal: 300000,
points: [6820,7540,8930,10220,11840,9650,8420,7340,9680,10420,8890,9120,10330,11540,10980,8450,7920,9380,10110,12040,11420,9860,8750,9640,10820,11360,9240,10150,8350,1869],
totalMode: 'sum',
yTicks: [12000, 9000, 6000, 3000, 0],
xLabels: ['1日','7日','14日','21日','30日']
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
goal: 3650000,
points: [86210, 91420, 95860, 278509, 102340, 98760, 104220, 109680, 113450, 118020, 110340, 106580],
totalMode: 'sum',
yTicks: [300000, 225000, 150000, 75000, 0],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月']
}
};
function formatNumber(num) {
return Number(num).toLocaleString('zh-CN');
}
function getTotal(cfg) {
if (cfg.totalMode === 'last') return cfg.points[cfg.points.length - 1];
return cfg.points.reduce(function(sum, item) { return sum + item; }, 0);
}
function getDistance(total) {
return (total * 0.0007116).toFixed(2);
}
function getCalories(total) {
return Math.round(total * 0.02943);
}
function renderChart(cfg) {
var chartWidth = 330;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('stepLine');
var dotEl = document.getElementById('stepDot');
grid.innerHTML = '';
cfg.yTicks.forEach(function(item, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(item, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatNumber(item) + '</span>';
}).join('');
var points = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var d = points.map(function(point, index) {
return (index === 0 ? 'M' : 'L') + point.x.toFixed(1) + ',' + point.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = points[points.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
xAxis.innerHTML = cfg.xLabels.map(function(label) {
return '<span>' + label + '</span>';
}).join('');
}
function renderMode(mode) {
var cfg = dataMap[mode];
var total = getTotal(cfg);
var distance = getDistance(total);
var calories = getCalories(total);
var eggs = Math.max(1, Math.round(calories / 55));
var laps = (Number(distance) / 0.4).toFixed(2);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('totalSteps').textContent = formatNumber(total);
document.getElementById('calorieValue').textContent = formatNumber(calories);
document.getElementById('distanceValue').textContent = distance;
document.getElementById('calorieCompare').textContent = '相当于消耗 ' + eggs + ' 个鸡蛋';
document.getElementById('distanceCompare').textContent = '相当于走了 ' + laps + ' 圈标准操场';
renderChart(cfg);
}
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
};
});
renderMode('day');
</script>
</body>
</html>
@@ -0,0 +1,362 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>压力预警历史 - 健康监测 | 健康长庆原型</title>
<style>
/* 全局重置 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* 手机外壳 */
.phone-shell {
width: 390px;
height: 844px;
background: #f5f7fa;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* 状态栏 */
.status-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
background: #fff;
}
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
padding: 0 16px;
flex-shrink: 0;
border-bottom: 1px solid #f0f0f0;
}
.nav-back {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-right: 28px;
}
/* 滚动内容区 */
.scroll-content {
flex: 1;
overflow-y: auto;
background: #f5f7fa;
}
.scroll-content::-webkit-scrollbar { display: none; }
/* 预警统计卡片 */
.stats-card {
margin: 12px 12px 0;
background: #fff;
border-radius: 14px;
padding: 20px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.stats-card__left { display: flex; flex-direction: column; align-items: flex-start; }
.stats-card__label { font-size: 13px; color: #999; margin-bottom: 6px; }
.stats-card__count-row { display: flex; align-items: baseline; gap: 4px; }
/* 压力预警统计数字使用橙色 */
.stats-card__count { font-size: 40px; font-weight: 800; color: #fa8c16; line-height: 1; }
.stats-card__unit { font-size: 14px; color: #fa8c16; font-weight: 600; }
.stats-card__right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.stats-card__recent-label { font-size: 12px; color: #999; }
.stats-card__recent-time { font-size: 13px; font-weight: 600; color: #333; }
/* 预警图标装饰(橙色背景圆形) */
.stats-card__icon {
width: 48px;
height: 48px;
background: #fff7e6;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
/* 列表区块标题 */
.section-title {
font-size: 13px;
color: #999;
padding: 14px 16px 8px;
font-weight: 500;
}
/* 预警记录卡片 */
.alert-card {
margin: 0 12px 10px;
background: #fff;
border-radius: 14px;
padding: 14px 14px 14px 12px;
display: flex;
gap: 12px;
align-items: flex-start;
}
/* 左侧等级圆点列 */
.alert-card__dot-col {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3px;
gap: 4px;
}
.alert-card__dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
/* 严重:红色圆点 */
.alert-card__dot--critical { background: #ff4d4f; box-shadow: 0 0 0 3px rgba(255,77,79,0.18); }
/* 警告:黄色圆点 */
.alert-card__dot--warning { background: #faad14; box-shadow: 0 0 0 3px rgba(250,173,20,0.18); }
/* 卡片主体内容 */
.alert-card__body { flex: 1; min-width: 0; }
.alert-card__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.alert-card__time { font-size: 13px; color: #666; }
.alert-card__badge {
font-size: 11px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
flex-shrink: 0;
}
/* 严重标签:红色 */
.alert-card__badge--critical { background: #fff1f0; color: #ff4d4f; }
/* 警告标签:黄色 */
.alert-card__badge--warning { background: #fffbe6; color: #d48806; }
/* 监测值行 */
.alert-card__value-row {
display: flex;
align-items: baseline;
gap: 4px;
margin-bottom: 6px;
}
.alert-card__value { font-size: 24px; font-weight: 800; color: #1a1a1a; line-height: 1; }
.alert-card__value-unit { font-size: 13px; color: #999; }
/* 详情行(持续时长等) */
.alert-card__detail-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.alert-card__detail-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #999;
}
.alert-card__detail-item svg { flex-shrink: 0; }
/* 触发原因说明框 */
.alert-card__reason {
margin-top: 8px;
font-size: 12px;
color: #bbb;
background: #fafafa;
border-radius: 6px;
padding: 6px 8px;
line-height: 1.5;
}
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<!-- WiFi 图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
<circle cx="12" cy="20" r="1" fill="#333" stroke="none"/>
</svg>
<!-- 电池图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<rect x="1" y="6" width="18" height="12" rx="2"/>
<path d="M21 10v4"/>
<rect x="3" y="8" width="12" height="8" rx="1" fill="#333" stroke="none"/>
</svg>
</div>
</div>
<!-- 导航栏:返回压力详情页 -->
<div class="nav-bar">
<div class="nav-back" onclick="location.href='stress.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round">
<path d="M15 19l-7-7 7-7"/>
</svg>
</div>
<div class="nav-title">压力预警历史</div>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<!-- 本月预警次数:橙色大字 -->
<div class="stats-card__count-row">
<span class="stats-card__count">2</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 压力预警图标:橙色脑波/压力符号 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<!-- 简化压力波形图标 -->
<path d="M3 12 L6 7 L9 15 L12 9 L15 14 L18 10 L21 12"
stroke="#fa8c16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-30 16:30</div>
</div>
</div>
<!-- 预警记录列表标题 -->
<div class="section-title">预警记录(共 2 条)</div>
<!-- 预警记录 1:警告 - 2026-04-30 16:30,压力值 82 -->
<div class="alert-card">
<!-- 左侧警告等级黄色圆点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 警告等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-30 16:30</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 压力监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">82</span>
<span class="alert-card__value-unit">压力值</span>
</div>
<!-- 持续时长 + 压力标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 25 分钟</span>
</div>
<!-- 压力类型标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M3 12 L6 8 L9 14 L12 9 L15 13 L18 10 L21 12"/>
</svg>
<span>压力</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:压力值超过 80 阈值</div>
</div>
</div>
<!-- 预警记录 2:严重 - 2026-04-22 15:10,压力值 88 -->
<div class="alert-card">
<!-- 左侧严重等级红色圆点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 严重等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-22 15:10</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 压力监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">88</span>
<span class="alert-card__value-unit">压力值</span>
</div>
<!-- 持续时长 + 压力标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 40 分钟</span>
</div>
<!-- 压力类型标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M3 12 L6 8 L9 14 L12 9 L15 13 L18 10 L21 12"/>
</svg>
<span>压力</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:压力值超过 85 阈值</div>
</div>
</div>
<!-- 底部安全区 -->
<div class="bottom-safe"></div>
</div><!-- /scroll-content -->
</div><!-- /phone-shell -->
</body>
</html>
+315
View File
@@ -0,0 +1,315 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>压力详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 主题色(压力橙) */
:root { --theme: #faad14; --theme2: #ffc53d; }
.top-panel { background: #fff; padding-top: 2px; }
.time-tabs { display: flex; justify-content: center; gap: 42px; padding: 2px 0 8px; }
.time-tab { position: relative; font-size: 18px; color: #444; cursor: pointer; padding: 0 2px 6px; user-select: none; }
.time-tab.active { color: var(--theme); }
.time-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; margin: auto; width: 22px; height: 3px; border-radius: 2px; background: var(--theme); }
.summary-area { text-align: center; padding: 12px 24px 6px; }
.date-row { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; color: #666; font-weight: 500; }
.range-text { margin-top: 4px; font-size: 16px; color: #666; }
.main-row { margin-top: 2px; display: inline-flex; align-items: baseline; gap: 4px; }
.main-row__value { font-size: 48px; line-height: 1; font-weight: 500; color: #1f1f1f; letter-spacing: -1px; }
.main-row__unit { font-size: 18px; color: #333; }
.main-badge { display: inline-flex; align-items: center; justify-content: center; margin-top: 6px; padding: 3px 12px; border-radius: 18px; font-size: 12px; font-weight: 600; background: #fffbe6; color: #d48806; }
.level-tip { margin-top: 12px; padding: 12px 0; font-size: 12px; line-height: 1.6; color: #666; }
.chart-wrap { position: relative; background: #fff; padding: 6px 16px 0; }
.chart-box { position: relative; height: 160px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: var(--theme); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: var(--theme); }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 0; height: 24px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 10px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: -4px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.card { margin: 8px 12px 0; background: #fff; border-radius: 22px; padding: 14px 12px; display: flex; gap: 0; }
.metric-row { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 6px; }
.metric-left__label { font-size: 12px; color: #777; margin-bottom: 4px; }
.metric-left__value { display: flex; align-items: baseline; gap: 2px; }
.metric-left__num { font-size: 16px; font-weight: 600; color: #222; }
.metric-left__unit { font-size: 12px; color: #777; }
.metric-right { font-size: 12px; color: #777; text-align: center; }
.metric-split { width: 1px; background: #ececec; margin: 0; }
.alert-btn { margin: 8px 12px 0; border: 1.5px solid var(--theme); border-radius: 12px; padding: 11px; text-align: center; font-size: 13px; color: var(--theme); font-weight: 600; cursor: pointer; background: #fff; display: flex; align-items: center; justify-content: center; gap: 6px; }
.foot-note { padding: 10px 0 12px; text-align: center; font-size: 11px; color: #b3b3b3; }
.bottom-safe { height: 12px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">压力</div>
</div>
<div class="scroll-content">
<div class="top-panel">
<div class="time-tabs">
<div class="time-tab active" data-mode="day"></div>
<div class="time-tab" data-mode="week"></div>
<div class="time-tab" data-mode="month"></div>
<div class="time-tab" data-mode="year"></div>
</div>
<div class="summary-area">
<div class="date-row">
<span id="dateText">4月30日 周四</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="#666"><path d="M7 10l5 5 5-5z"/></svg>
</div>
<div class="range-text" id="rangeText">00:00-23:59</div>
<div class="main-row">
<span class="main-row__value" id="mainValue">42</span>
<span class="main-row__unit" id="mainUnit"></span>
</div>
<div class="main-badge" id="statusBadge">放松</div>
<div class="level-tip">压力等级说明:1-29放松 / 30-59正常 / 60-79中等 / 80-100偏高</div>
</div>
<div class="chart-wrap">
<div class="chart-box">
<svg class="chart-svg" id="chart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="line" class="chart-path"></path>
<circle id="dot" class="chart-dot" r="3.2"></circle>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
</div>
</div>
</div>
<div class="card">
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最高</div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">82</span><span class="metric-left__unit"></span></div>
</div>
<div class="metric-right" id="maxHint">16:30</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最低</div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">18</span><span class="metric-left__unit"></span></div>
</div>
<div class="metric-right" id="minHint">05:10</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">平均</div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">46</span><span class="metric-left__unit"></span></div>
</div>
<div class="metric-right" id="avgHint">全天</div>
</div>
</div>
<div class="alert-btn" onclick="location.href='stress-alerts.html'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--theme)" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
<div class="foot-note">以上数据为原型示例,实际以设备同步为准</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
unit: '分',
decimals: 0,
points: [35,32,30,28,24,18,22,26,34,40,52,60,68,74,82,78,66,58,49,45,41,38,36,34],
totalMode: 'last',
yTicks: [100, 80, 60, 40, 20, 0],
xLabels: ['0:00','6:00','12:00','18:00','24:00'],
status: '放松',
max: { v:82, hint:'16:30' },
min: { v:18, hint:'05:10' },
avg: { v:46, hint:'全天' }
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: '分',
decimals: 0,
points: [45,52,38,61,42,55,42],
totalMode: 'avg',
yTicks: [100, 80, 60, 40, 20, 0],
xLabels: ['周一','周二','周三','周四','周五','周六','周日'],
status: '正常',
max: { v:61, hint:'周四' },
min: { v:38, hint:'周三' },
avg: { v:48, hint:'本周' }
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: '分',
decimals: 0,
points: [42,45,38,48,41,52,39,44,47,50,48,45,41,38,51,53,49,46,43,40,38,42,45,48,51,48,45,42,39,46],
totalMode: 'avg',
yTicks: [100, 80, 60, 40, 20, 0],
xLabels: ['1日','7日','14日','21日','30日'],
status: '正常',
max: { v:53, hint:'04-16' },
min: { v:38, hint:'04-08' },
avg: { v:45, hint:'本月' }
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: '分',
decimals: 0,
points: [48,45,42,46,48,45,42,45,48,51,49,46],
totalMode: 'avg',
yTicks: [100, 80, 60, 40, 20, 0],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
status: '正常',
max: { v:51, hint:'10月' },
min: { v:42, hint:'03月' },
avg: { v:46, hint:'全年' }
}
};
function avg(arr) {
if (!arr.length) return 0;
var sum = arr.reduce(function(s, v){ return s + v; }, 0);
return sum / arr.length;
}
function getValue(cfg) {
if (cfg.totalMode === 'last') return cfg.points[cfg.points.length - 1];
if (cfg.totalMode === 'avg') return avg(cfg.points);
if (cfg.totalMode === 'max') return Math.max.apply(null, cfg.points);
if (cfg.totalMode === 'min') return Math.min.apply(null, cfg.points);
return cfg.points[cfg.points.length - 1];
}
function formatValue(val, decimals) {
if (typeof val !== 'number') return String(val);
if (decimals != null) return val.toFixed(decimals);
return String(val);
}
function renderChart(cfg) {
var chartWidth = 330;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('line');
var dotEl = document.getElementById('dot');
grid.innerHTML = '';
cfg.yTicks.forEach(function(_, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(tick, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatValue(tick, 0) + '</span>';
}).join('');
var pts = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var d = pts.map(function(p, index) {
return (index === 0 ? 'M' : 'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = pts[pts.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
xAxis.innerHTML = cfg.xLabels.map(function(label) {
return '<span>' + label + '</span>';
}).join('');
}
function renderMode(mode) {
var cfg = dataMap[mode];
var value = getValue(cfg);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, cfg.decimals);
document.getElementById('maxHint').textContent = cfg.max.hint;
document.getElementById('minValue').textContent = formatValue(cfg.min.v, cfg.decimals);
document.getElementById('minHint').textContent = cfg.min.hint;
document.getElementById('avgValue').textContent = formatValue(cfg.avg.v, cfg.decimals);
document.getElementById('avgHint').textContent = cfg.avg.hint;
renderChart(cfg);
}
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
};
});
renderMode('day');
</script>
</body>
</html>
@@ -0,0 +1,314 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体温预警历史 - 健康监测 | 健康长庆原型</title>
<style>
/* 全局重置 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* 手机外壳 */
.phone-shell {
width: 390px;
height: 844px;
background: #f5f7fa;
border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* 状态栏 */
.status-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
background: #fff;
}
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
/* 导航栏 */
.nav-bar {
height: 44px;
background: #fff;
display: flex;
align-items: center;
padding: 0 16px;
flex-shrink: 0;
border-bottom: 1px solid #f0f0f0;
}
.nav-back {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-right: 28px;
}
/* 滚动内容区 */
.scroll-content {
flex: 1;
overflow-y: auto;
background: #f5f7fa;
}
.scroll-content::-webkit-scrollbar { display: none; }
/* 预警统计卡片 */
.stats-card {
margin: 12px 12px 0;
background: #fff;
border-radius: 14px;
padding: 20px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.stats-card__left { display: flex; flex-direction: column; align-items: flex-start; }
.stats-card__label { font-size: 13px; color: #999; margin-bottom: 6px; }
.stats-card__count-row { display: flex; align-items: baseline; gap: 4px; }
.stats-card__count { font-size: 40px; font-weight: 800; color: #faad14; line-height: 1; }
.stats-card__unit { font-size: 14px; color: #faad14; font-weight: 600; }
.stats-card__right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.stats-card__recent-label { font-size: 12px; color: #999; }
.stats-card__recent-time { font-size: 13px; font-weight: 600; color: #333; }
/* 预警图标装饰 */
.stats-card__icon {
width: 48px;
height: 48px;
background: #fffbe6;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
/* 列表区块标题 */
.section-title {
font-size: 13px;
color: #999;
padding: 14px 16px 8px;
font-weight: 500;
}
/* 预警记录卡片 */
.alert-card {
margin: 0 12px 10px;
background: #fff;
border-radius: 14px;
padding: 14px 14px 14px 12px;
display: flex;
gap: 12px;
align-items: flex-start;
}
/* 左侧等级圆点 */
.alert-card__dot-col {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3px;
gap: 4px;
}
.alert-card__dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.alert-card__dot--critical { background: #ff4d4f; box-shadow: 0 0 0 3px rgba(255,77,79,0.18); }
.alert-card__dot--warning { background: #faad14; box-shadow: 0 0 0 3px rgba(250,173,20,0.18); }
/* 卡片主体内容 */
.alert-card__body { flex: 1; min-width: 0; }
.alert-card__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.alert-card__time { font-size: 13px; color: #666; }
.alert-card__badge {
font-size: 11px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
flex-shrink: 0;
}
.alert-card__badge--critical { background: #fff1f0; color: #ff4d4f; }
.alert-card__badge--warning { background: #fffbe6; color: #d48806; }
/* 监测值 */
.alert-card__value-row {
display: flex;
align-items: baseline;
gap: 4px;
margin-bottom: 6px;
}
.alert-card__value { font-size: 24px; font-weight: 800; color: #1a1a1a; line-height: 1; }
.alert-card__value-unit { font-size: 13px; color: #999; }
/* 详情行 */
.alert-card__detail-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.alert-card__detail-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #999;
}
.alert-card__detail-item svg { flex-shrink: 0; }
/* 触发原因 */
.alert-card__reason {
margin-top: 8px;
font-size: 12px;
color: #bbb;
background: #fafafa;
border-radius: 6px;
padding: 6px 8px;
line-height: 1.5;
}
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<!-- WiFi 图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
<circle cx="12" cy="20" r="1" fill="#333" stroke="none"/>
</svg>
<!-- 电池图标 -->
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2">
<rect x="1" y="6" width="18" height="12" rx="2"/>
<path d="M21 10v4"/>
<rect x="3" y="8" width="12" height="8" rx="1" fill="#333" stroke="none"/>
</svg>
</div>
</div>
<!-- 导航栏:返回体温详情页 -->
<div class="nav-bar">
<div class="nav-back" onclick="location.href='temperature.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round">
<path d="M15 19l-7-7 7-7"/>
</svg>
</div>
<div class="nav-title">体温预警历史</div>
</div>
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">1</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 体温预警图标 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z" stroke="#faad14" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-15 13:28</div>
</div>
</div>
<!-- 预警记录列表标题 -->
<div class="section-title">预警记录(共 1 条)</div>
<!-- 预警记录:警告 - 2026-04-15 -->
<div class="alert-card">
<!-- 左侧警告等级黄点 -->
<div class="alert-card__dot-col">
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 警告等级标签 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-15 13:28</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 体温监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">37.8</span>
<span class="alert-card__value-unit">°C</span>
</div>
<!-- 持续时长 + 体温标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 45 分钟</span>
</div>
<!-- 体温标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z"/>
</svg>
<span>体温</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:体温超过 37.3°C 阈值</div>
</div>
</div>
<!-- 底部安全区 -->
<div class="bottom-safe"></div>
</div><!-- /scroll-content -->
</div><!-- /phone-shell -->
</body>
</html>
@@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体温详情 - 健康监测 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 主题色(体温紫) */
:root { --theme: #722ed1; --theme2: #b37feb; }
.top-panel { background: #fff; padding-top: 2px; }
.time-tabs { display: flex; justify-content: center; gap: 42px; padding: 2px 0 8px; }
.time-tab { position: relative; font-size: 18px; color: #444; cursor: pointer; padding: 0 2px 6px; user-select: none; }
.time-tab.active { color: var(--theme); }
.time-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; margin: auto; width: 22px; height: 3px; border-radius: 2px; background: var(--theme); }
.summary-area { text-align: center; padding: 12px 24px 6px; }
.date-row { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; color: #666; font-weight: 500; }
.range-text { margin-top: 4px; font-size: 16px; color: #666; }
.main-row { margin-top: 2px; display: inline-flex; align-items: baseline; gap: 4px; }
.main-row__value { font-size: 48px; line-height: 1; font-weight: 500; color: #1f1f1f; letter-spacing: -1px; }
.main-row__unit { font-size: 18px; color: #333; }
.main-badge { display: inline-flex; align-items: center; justify-content: center; margin-top: 6px; padding: 3px 12px; border-radius: 18px; font-size: 12px; font-weight: 600; background: #f6ffed; color: #52c41a; }
.chart-wrap { position: relative; background: #fff; padding: 6px 16px 0; }
.chart-box { position: relative; height: 160px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: var(--theme); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: var(--theme); }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 0; height: 24px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 10px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: -4px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.card { margin: 8px 12px 0; background: #fff; border-radius: 22px; padding: 14px 12px; display: flex; gap: 0; }
.metric-row { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 6px; }
.metric-left__label { font-size: 12px; color: #777; margin-bottom: 4px; }
.metric-left__value { display: flex; align-items: baseline; gap: 2px; }
.metric-left__num { font-size: 16px; font-weight: 600; color: #222; }
.metric-left__unit { font-size: 12px; color: #777; }
.metric-right { font-size: 12px; color: #777; text-align: center; }
.metric-split { width: 1px; background: #ececec; margin: 0; }
.alert-btn { margin: 8px 12px 0; border: 1.5px solid var(--theme); border-radius: 12px; padding: 11px; text-align: center; font-size: 13px; color: var(--theme); font-weight: 600; cursor: pointer; background: #fff; display: flex; align-items: center; justify-content: center; gap: 6px; }
.foot-note { padding: 10px 0 12px; text-align: center; font-size: 11px; color: #b3b3b3; }
.bottom-safe { height: 12px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><path d="M1 6l5 5 5-5"/><path d="M4 9l3 3 3-3" opacity="0.5"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><rect x="1" y="6" width="18" height="12" rx="2"/><path d="M21 10v4"/></svg>
</div>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="location.href='index.html'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round"><path d="M15 19l-7-7 7-7"/></svg>
</div>
<div class="nav-title">体温</div>
</div>
<div class="scroll-content">
<div class="top-panel">
<div class="time-tabs">
<div class="time-tab active" data-mode="day"></div>
<div class="time-tab" data-mode="week"></div>
<div class="time-tab" data-mode="month"></div>
<div class="time-tab" data-mode="year"></div>
</div>
<div class="summary-area">
<div class="date-row">
<span id="dateText">4月30日 周四</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="#666"><path d="M7 10l5 5 5-5z"/></svg>
</div>
<div class="range-text" id="rangeText">00:00-23:59</div>
<div class="main-row">
<span class="main-row__value" id="mainValue">36.5</span>
<span class="main-row__unit" id="mainUnit">°C</span>
</div>
<div class="main-badge" id="statusBadge">正常</div>
</div>
<div class="chart-wrap">
<div class="chart-box">
<svg class="chart-svg" id="chart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="line" class="chart-path"></path>
<circle id="dot" class="chart-dot" r="3.2"></circle>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
</div>
</div>
</div>
<div class="card">
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最高</div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">37.1</span><span class="metric-left__unit">°C</span></div>
</div>
<div class="metric-right" id="maxHint">13:00</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最低</div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">36.2</span><span class="metric-left__unit">°C</span></div>
</div>
<div class="metric-right" id="minHint">04:20</div>
</div>
<div class="metric-split"></div>
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">平均</div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">36.6</span><span class="metric-left__unit">°C</span></div>
</div>
<div class="metric-right" id="avgHint">全天</div>
</div>
</div>
<div class="alert-btn" onclick="location.href='temperature-alerts.html'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--theme)" stroke-width="2"><path d="M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
查看预警历史
</div>
<div class="foot-note">以上数据为原型示例,实际以设备同步为准</div>
<div class="bottom-safe"></div>
</div>
</div>
<script>
var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
unit: '°C',
decimals: 1,
points: [36.5,36.4,36.4,36.3,36.2,36.2,36.3,36.4,36.5,36.5,36.6,36.7,36.8,37.1,36.9,36.8,36.7,36.6,36.6,36.5,36.5,36.4,36.4,36.5],
totalMode: 'last',
yTicks: [37.5, 37.0, 36.5, 36.0, 35.5],
xLabels: ['00:00','06:00','12:00','18:00','24:00'],
status: '正常',
max: { v:37.1, hint:'13:00' },
min: { v:36.2, hint:'04:20' },
avg: { v:36.6, hint:'全天' }
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: '°C',
decimals: 1,
points: [36.5, 36.6, 36.4, 36.7, 36.5, 36.8, 36.5],
totalMode: 'avg',
yTicks: [37.5, 37.0, 36.5, 36.0, 35.5],
xLabels: ['周一','周二','周三','周四','周五','周六','周日'],
status: '正常',
max: { v:36.8, hint:'周六' },
min: { v:36.4, hint:'周三' },
avg: { v:36.6, hint:'本周' }
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: '°C',
decimals: 1,
points: [36.5,36.6,36.4,36.7,36.5,36.8,36.5,36.6,36.4,36.7,36.5,36.8,36.5,36.6,36.4,36.7,36.5,36.8,36.5,36.6,36.4,36.7,36.5,36.8,36.5,36.6,36.4,36.7,36.5,36.6],
totalMode: 'avg',
yTicks: [37.5, 37.0, 36.5, 36.0, 35.5],
xLabels: ['1日','7日','14日','21日','30日'],
status: '正常',
max: { v:37.1, hint:'04-15' },
min: { v:36.2, hint:'04-08' },
avg: { v:36.6, hint:'本月' }
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: '°C',
decimals: 1,
points: [36.5,36.6,36.4,36.7,36.5,36.8,36.5,36.6,36.4,36.7,36.5,36.6],
totalMode: 'avg',
yTicks: [37.5, 37.0, 36.5, 36.0, 35.5],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
status: '正常',
max: { v:37.2, hint:'03月' },
min: { v:36.2, hint:'02月' },
avg: { v:36.6, hint:'全年' }
}
};
function avg(arr) {
if (!arr.length) return 0;
var sum = arr.reduce(function(s, v){ return s + v; }, 0);
return sum / arr.length;
}
function getValue(cfg) {
if (cfg.totalMode === 'last') return cfg.points[cfg.points.length - 1];
if (cfg.totalMode === 'avg') return avg(cfg.points);
if (cfg.totalMode === 'max') return Math.max.apply(null, cfg.points);
if (cfg.totalMode === 'min') return Math.min.apply(null, cfg.points);
return cfg.points[cfg.points.length - 1];
}
function formatValue(val, decimals) {
if (typeof val !== 'number') return String(val);
if (decimals != null) return val.toFixed(decimals);
return String(val);
}
function renderChart(cfg) {
var chartWidth = 330;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('line');
var dotEl = document.getElementById('dot');
grid.innerHTML = '';
cfg.yTicks.forEach(function(_, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(tick, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatValue(tick, 1) + '</span>';
}).join('');
var pts = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var d = pts.map(function(p, index) {
return (index === 0 ? 'M' : 'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = pts[pts.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
xAxis.innerHTML = cfg.xLabels.map(function(label) {
return '<span>' + label + '</span>';
}).join('');
}
function renderMode(mode) {
var cfg = dataMap[mode];
var value = getValue(cfg);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, cfg.decimals);
document.getElementById('maxHint').textContent = cfg.max.hint;
document.getElementById('minValue').textContent = formatValue(cfg.min.v, cfg.decimals);
document.getElementById('minHint').textContent = cfg.min.hint;
document.getElementById('avgValue').textContent = formatValue(cfg.avg.v, cfg.decimals);
document.getElementById('avgHint').textContent = cfg.avg.hint;
renderChart(cfg);
}
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
};
});
renderMode('day');
</script>
</body>
</html>