Files
healthflow/healthflow_minimal_ui.html
2026-01-02 17:35:32 +08:00

782 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HealthFlow - 极致简约健康管理</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #f8fafc;
--card-bg: #ffffff;
--text-primary: #0f172a;
--text-secondary: #64748b;
--border-color: #e2e8f0;
--accent-green: #059669;
--accent-red: #dc2626;
--card-padding: 20px;
--border-radius: 16px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
display: flex;
justify-content: center;
min-height: 100vh;
overflow-x: hidden;
}
.app-container {
width: 100%;
max-width: 450px;
min-height: 100vh;
position: relative;
background: white;
padding-bottom: 80px;
}
.screen {
display: none;
padding: 24px;
animation: fadeIn 0.3s ease-in-out;
}
.screen.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
header {
margin-bottom: 32px;
}
h1 {
font-size: 28px;
font-weight: 700;
letter-spacing: -0.5px;
}
.subtitle {
font-size: 14px;
color: var(--text-secondary);
margin-top: 4px;
}
.card {
position: relative;
background: var(--card-bg);
border-radius: var(--border-radius);
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 10px 15px -3px rgba(15, 23, 42, 0.05);
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
cursor: pointer;
border: 1px solid rgba(226, 232, 240, 0.8);
overflow: hidden;
}
.card:active {
transform: scale(0.97);
}
.card-accent {
position: absolute;
left: 0;
top: 24px;
bottom: 24px;
width: 4px;
background: var(--accent-green);
border-radius: 0 4px 4px 0;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 8px;
}
.card-title {
font-weight: 700;
font-size: 19px;
letter-spacing: -0.3px;
color: var(--text-primary);
}
.card-status {
font-size: 14px;
font-weight: 600;
color: var(--accent-green);
opacity: 0.9;
}
.card-dates {
font-size: 14px;
font-weight: 500;
color: var(--text-secondary);
margin-bottom: 18px;
}
.progress-container {
height: 6px;
background: #f1f5f9;
border-radius: 10px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: var(--text-primary);
width: 0%;
border-radius: 10px;
transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}
.dashboard {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
padding: 10px 0;
}
.stat-item {
text-align: left;
}
.stat-value {
font-size: 22px;
font-weight: 700;
display: block;
}
.stat-label {
font-size: 12px;
color: var(--text-secondary);
text-transform: uppercase;
}
.week-list {
margin-top: 24px;
}
.week-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0;
border-bottom: 1px solid var(--border-color);
}
.week-info {
display: flex;
flex-direction: column;
}
.week-name {
font-weight: 500;
}
.week-date {
font-size: 12px;
color: var(--text-secondary);
}
.week-result {
font-weight: 600;
color: var(--accent-green);
}
.week-result.gain {
color: var(--accent-red);
}
.hero-weight {
text-align: center;
margin: 60px 0;
}
.hero-value {
font-size: 72px;
font-weight: 700;
letter-spacing: -2px;
}
.hero-unit {
font-size: 24px;
color: var(--text-secondary);
margin-left: 4px;
}
.btn-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.btn-outline {
width: 100%;
padding: 16px;
border: 1px solid var(--text-primary);
background: transparent;
color: var(--text-primary);
border-radius: var(--border-radius);
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn-outline:active {
background: var(--text-primary);
color: white;
}
.stamp {
position: absolute;
top: 50%;
right: -10px;
transform: translateY(-50%) rotate(-15deg);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
user-select: none;
pointer-events: none;
z-index: 0;
opacity: 0.12;
mix-blend-mode: darken;
}
.stamp svg {
width: 80px;
height: 80px;
margin-bottom: -5px;
}
.stamp .stamp-text {
font-weight: 850;
font-size: 32px;
letter-spacing: 2px;
white-space: nowrap;
}
.stamp-pass {
color: var(--accent-green);
}
.stamp-fail {
color: var(--accent-red);
}
.profile-header {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 40px;
}
.avatar {
width: 72px;
height: 72px;
border-radius: 50%;
background: #e2e8f0;
border: 3px solid white;
box-shadow: 0 10px 15px -3px rgba(15, 23, 42, 0.1);
}
.user-info h2 {
font-size: 24px;
font-weight: 800;
letter-spacing: -0.5px;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 24px;
}
.dark-card {
background: var(--text-primary);
color: white;
padding: 32px 24px;
border-radius: var(--border-radius);
position: relative;
overflow: hidden;
border: none;
}
.year-badge {
position: absolute;
top: 0;
right: 0;
background: rgba(255, 255, 255, 0.1);
padding: 8px 16px;
border-radius: 0 0 0 16px;
font-size: 12px;
font-weight: 600;
}
/* 表单样式 */
.form-group {
margin-bottom: 24px;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.form-input {
width: 100%;
padding: 16px;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
font-size: 16px;
font-family: inherit;
color: var(--text-primary);
background: var(--card-bg);
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--text-primary);
}
/* 悬浮按钮 (FAB) - 约束在容器内 */
.fab {
position: absolute;
bottom: 100px;
right: 24px;
width: 60px;
height: 60px;
background: var(--text-primary);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
font-weight: 500;
box-shadow: 0 10px 15px -3px rgba(15, 23, 42, 0.25);
cursor: pointer;
z-index: 99;
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.fab:active {
transform: scale(0.9);
}
.bottom-nav {
position: fixed;
bottom: 0;
width: 100%;
max-width: 450px;
height: 80px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-top: 1px solid var(--border-color);
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
z-index: 100;
}
.nav-item {
flex: 1;
text-align: center;
color: var(--text-secondary);
cursor: pointer;
padding: 10px 0;
font-weight: 500;
font-size: 12px;
display: flex;
flex-direction: column;
align-items: center;
}
.nav-item.active {
color: var(--text-primary);
}
.nav-icon {
display: block;
margin-bottom: 4px;
font-size: 20px;
}
</style>
</head>
<body>
<div class="app-container">
<!-- 页面 1: 首页 -->
<div id="home" class="screen active">
<header>
<h1>HealthFlow</h1>
<p class="subtitle">追踪你的健康演变</p>
</header>
<div class="card" onclick="showScreen('epoch-detail')">
<div class="card-accent"></div>
<div class="card-header">
<span class="card-title">寒武纪元</span>
<span class="card-status">进行中</span>
</div>
<p class="card-dates">2025.12.21 - 2026.02.14</p>
<div class="progress-container">
<div class="progress-bar" style="width: 25%"></div>
</div>
</div>
<!-- 右下角悬浮按钮 -->
<div class="fab" onclick="showScreen('create-epoch')">+</div>
<div class="card">
<div class="card-header">
<span class="card-title">创世纪元</span>
</div>
<div class="stamp stamp-pass">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"
stroke-linejoin="round">
<path d="M20 6L9 17l-5-5" />
</svg>
<span class="stamp-text">合格</span>
</div>
<p class="card-dates">2025.10.01 - 2025.12.20</p>
<div class="progress-container">
<div class="progress-bar" style="width: 100%"></div>
</div>
</div>
</div>
<!-- 页面 2: 创建纪元 -->
<div id="create-epoch" class="screen">
<header>
<div style="cursor:pointer; margin-bottom: 8px;" onclick="showScreen('home')">← 取消</div>
<h1>开启新纪元</h1>
<p class="subtitle">为你的健康阶段设定一个开端</p>
</header>
<div class="form-group">
<label class="form-label">纪元名称</label>
<input type="text" class="form-input" placeholder="例如:盛夏减脂阶段">
</div>
<div class="form-group">
<label class="form-label">起始日期</label>
<input type="date" class="form-input">
</div>
<div class="form-group">
<label class="form-label">截止日期</label>
<input type="date" class="form-input">
</div>
<div style="margin-top: 40px;">
<button class="btn-outline" style="background: var(--text-primary); color: white;"
onclick="showScreen('home')">确认开启</button>
</div>
</div>
<!-- 页面 3: 计划 -->
<div id="plans" class="screen">
<header>
<h1>计划中心</h1>
<p class="subtitle">专注于本阶段的周计划</p>
</header>
<div style="margin-bottom: 32px;">
<p class="stat-label" style="margin-bottom: 16px;">正在进行中</p>
<div class="card" onclick="showScreen('week-detail')">
<div class="card-accent" style="background: var(--accent-green);"></div>
<div class="card-header">
<span class="card-title">寒武纪元 - 第 2 周</span>
<span class="card-status">第 12 天</span>
</div>
<p class="card-dates">目标: 82.8 kg (还差 0.2 kg)</p>
<div class="progress-container">
<div class="progress-bar" style="width: 85%; background: var(--accent-green);"></div>
</div>
</div>
</div>
<div>
<p class="stat-label" style="margin-bottom: 12px;">后续序列</p>
<div class="week-item">
<div class="week-info">
<span class="week-name">第 3 周</span>
<span class="week-date">01.04 - 01.10</span>
</div>
<span class="week-result" style="color: var(--text-secondary)">待启动</span>
</div>
</div>
</div>
<!-- 页面 4: 详情 -->
<div id="epoch-detail" class="screen">
<header>
<div style="cursor:pointer; margin-bottom: 8px;" onclick="showScreen('home')">← 返回</div>
<h1>寒武纪元</h1>
<p class="subtitle">2025.12.21 - 2026.02.14</p>
</header>
<div class="dashboard">
<div class="stat-item">
<span class="stat-value">85.0</span>
<span class="stat-label">初始</span>
</div>
<div class="stat-item">
<span class="stat-value">75.0</span>
<span class="stat-label">目标</span>
</div>
<div class="stat-item">
<span class="stat-value">-2.4</span>
<span class="stat-label">已减</span>
</div>
</div>
<div class="week-list">
<div class="week-item" onclick="showScreen('week-detail')" style="position:relative; overflow:hidden;">
<div class="week-info">
<span class="week-name">第一周</span>
<span class="week-date">12.21 - 12.27</span>
</div>
<div class="stamp stamp-fail" style="right: 50px;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
<span class="stamp-text" style="font-size: 16px;">不合格</span>
</div>
<span class="week-result" style="color: var(--accent-red)">+0.5 kg</span>
</div>
</div>
</div>
<!-- 页面 5: 周计划详情 -->
<div id="week-detail" class="screen">
<header>
<div style="cursor:pointer; margin-bottom: 8px;" onclick="showScreen('plans')">← 返回</div>
<h1>第一周计划</h1>
<p class="subtitle">正在为了本周目标努力</p>
</header>
<div class="hero-weight">
<span class="hero-value">82.6</span>
<span class="hero-unit">kg</span>
<p class="subtitle">当前体重记录</p>
</div>
<div class="btn-group">
<button class="btn-outline" style="background: var(--text-primary); color: white;">添加体重记录</button>
<button class="btn-outline">修改周目标</button>
</div>
<div class="week-list" style="margin-top: 40px;">
<p class="stat-label">本周指标</p>
<div class="week-item">
<span class="week-name">周初始</span>
<span class="week-name">83.8 kg</span>
</div>
<div class="week-item">
<span class="week-name">周目标</span>
<span class="week-name">82.8 kg</span>
</div>
</div>
</div>
<!-- 页面 6: 我的 -->
<div id="profile" class="screen">
<header>
<h1>个人中心</h1>
</header>
<div class="profile-header">
<div class="avatar"><img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Amos" alt="avatar"
style="width:100%; height:100%; border-radius:50%;"></div>
<div class="user-info">
<h2>Amos</h2>
<p class="subtitle">坚持 452 天</p>
</div>
</div>
<div class="stats-grid">
<div class="card" style="margin-bottom:0; padding:20px;">
<span class="stat-label">今年减重</span>
<span class="stat-value" style="color:var(--accent-green); margin-top:8px;">12.5 kg</span>
</div>
<div class="card" style="margin-bottom:0; padding:20px;">
<span class="stat-label">总减重</span>
<span class="stat-value" style="margin-top:8px;">24.8 kg</span>
</div>
</div>
<div class="dark-card">
<div class="year-badge">2025 PROGRESS</div>
<span class="stat-label">今年剩余天数</span>
<div class="stat-value" style="font-size:42px; margin-top:8px;">09 <small
style="font-size:18px; opacity:0.7;">DAYS</small></div>
</div>
<div style="margin-top:40px;">
<div class="week-item"><span class="week-name" style="color:var(--accent-red)">退出登录</span></div>
</div>
</div>
<!-- 页面 7: 运动打卡 -->
<div id="exercise" class="screen">
<header>
<h1>运动</h1>
<p class="subtitle">记录你的运动轨迹</p>
</header>
<!-- 统计卡片 -->
<div style="display: flex; gap: 12px; margin-bottom: 24px;">
<div class="card" style="flex:1; margin-bottom:0; padding:16px; text-align:center;">
<span style="font-size:24px;">🏋️</span>
<span class="stat-value" style="display:block; margin-top:8px;">42</span>
<span class="stat-label">总打卡</span>
</div>
<div class="card" style="flex:1; margin-bottom:0; padding:16px; text-align:center;">
<span style="font-size:24px;">🔥</span>
<span class="stat-value" style="display:block; margin-top:8px; color:#f59e0b;">7</span>
<span class="stat-label">连续天数</span>
</div>
<div class="card" style="flex:1; margin-bottom:0; padding:16px; text-align:center;">
<span style="font-size:24px;">📅</span>
<span class="stat-value" style="display:block; margin-top:8px; color:var(--accent-green);">12</span>
<span class="stat-label">本月</span>
</div>
</div>
<!-- 季度选择器 -->
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
<span style="font-size:20px; cursor:pointer;"></span>
<div style="text-align:center;">
<div style="font-size:12px; color:var(--text-secondary); font-weight:600;">2026年</div>
<div style="font-size:20px; font-weight:700;">1月 - 3月</div>
</div>
<span style="font-size:20px; cursor:pointer; color:var(--text-secondary);"></span>
</div>
<!-- GitHub 风格热力图 -->
<div class="card" style="padding:20px;">
<div style="display:flex; justify-content:space-between; margin-bottom:12px;">
<span style="font-size:12px; color:var(--text-secondary);">1月</span>
<span style="font-size:12px; color:var(--text-secondary);">2月</span>
<span style="font-size:12px; color:var(--text-secondary);">3月</span>
</div>
<div style="display:flex; gap:3px; justify-content:center; flex-wrap:wrap;">
<!-- 热力图格子 - 模拟数据 -->
<script>
// 生成热力图格子
const colors = ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'];
for(let i = 0; i < 91; i++) {
const level = Math.random() > 0.6 ? Math.floor(Math.random() * 5) : 0;
document.write(`<div style="width:14px;height:14px;background:${colors[level]};border-radius:3px;"></div>`);
}
</script>
</div>
<div style="display:flex; justify-content:center; align-items:center; gap:8px; margin-top:16px;">
<span style="font-size:10px; color:var(--text-secondary);"></span>
<div style="width:10px;height:10px;background:#ebedf0;border-radius:2px;"></div>
<div style="width:10px;height:10px;background:#9be9a8;border-radius:2px;"></div>
<div style="width:10px;height:10px;background:#40c463;border-radius:2px;"></div>
<div style="width:10px;height:10px;background:#30a14e;border-radius:2px;"></div>
<div style="width:10px;height:10px;background:#216e39;border-radius:2px;"></div>
<span style="font-size:10px; color:var(--text-secondary);"></span>
</div>
</div>
<!-- 最近打卡记录 -->
<div style="margin-top:24px;">
<div style="display:flex; justify-content:space-between; margin-bottom:16px;">
<span style="font-weight:600;">最近打卡</span>
<span style="font-size:12px; color:var(--text-secondary);">共12条</span>
</div>
<div class="week-item">
<div style="display:flex; align-items:center; gap:12px;">
<div style="text-align:center; width:40px;">
<div style="font-size:12px; color:var(--text-secondary);">周五</div>
<div style="font-size:18px; font-weight:700;">2</div>
</div>
<div style="flex:1; background:#f8fafc; padding:12px; border-radius:12px;">
<div style="font-size:14px;">晨跑 5 公里 🏃</div>
<div style="font-size:12px; color:var(--text-secondary);">1月2日</div>
</div>
<div style="width:24px;height:24px;background:var(--accent-green);border-radius:50%;display:flex;align-items:center;justify-content:center;">
<span style="color:white;font-size:12px;"></span>
</div>
</div>
</div>
<div class="week-item">
<div style="display:flex; align-items:center; gap:12px;">
<div style="text-align:center; width:40px;">
<div style="font-size:12px; color:var(--text-secondary);">周四</div>
<div style="font-size:18px; font-weight:700;">1</div>
</div>
<div style="flex:1; background:#f8fafc; padding:12px; border-radius:12px;">
<div style="font-size:14px;">健身房力量训练 💪</div>
<div style="font-size:12px; color:var(--text-secondary);">1月1日</div>
</div>
<div style="width:24px;height:24px;background:var(--accent-green);border-radius:50%;display:flex;align-items:center;justify-content:center;">
<span style="color:white;font-size:12px;"></span>
</div>
</div>
</div>
</div>
<!-- 打卡按钮 -->
<div class="fab" style="background:var(--accent-green);" onclick="alert('打卡成功!')">+</div>
</div>
<nav class="bottom-nav">
<div class="nav-item active" onclick="showScreen('home')"><span class="nav-icon"></span>纪元</div>
<div class="nav-item" onclick="showScreen('plans')"><span class="nav-icon"></span>计划</div>
<div class="nav-item" onclick="showScreen('exercise')"><span class="nav-icon"></span>运动</div>
<div class="nav-item" onclick="showScreen('profile')"><span class="nav-icon"></span>我的</div>
</nav>
</div>
<script>
function showScreen(screenId) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(screenId).classList.add('active');
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
if (screenId === 'home' || screenId === 'create-epoch') {
document.querySelectorAll('.nav-item')[0].classList.add('active');
} else if (screenId === 'plans' || screenId === 'week-detail' || screenId === 'epoch-detail') {
document.querySelectorAll('.nav-item')[1].classList.add('active');
} else if (screenId === 'exercise') {
document.querySelectorAll('.nav-item')[2].classList.add('active');
} else if (screenId === 'profile') {
document.querySelectorAll('.nav-item')[3].classList.add('active');
}
window.scrollTo(0, 0);
}
</script>
</body>
</html>