feat: 首页仪表盘改造为深色科技风格 + Apple Health 步数同步

UI 改造:
- 首页采用深色科技风格 (Dark Tech)
- Tailwind CSS + Glass Morphism 毛玻璃效果
- 左侧边栏导航 (PC端) / 底部导航 (移动端)
- Material Symbols 图标 + Space Grotesk 字体
- Chart.js 深色主题适配

新功能:
- Apple Health 步数同步 API (/api/steps)
- 设置页面添加 iOS 快捷指令配置说明

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 16:57:13 +08:00
parent 3b1f7fb416
commit 02426e0a59
3 changed files with 685 additions and 488 deletions

View File

@@ -200,6 +200,22 @@ def init_db():
)
""")
# 步数记录表
cursor.execute("""
CREATE TABLE IF NOT EXISTS steps (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT DEFAULT 1,
date DATE NOT NULL,
steps INT NOT NULL DEFAULT 0,
distance FLOAT,
calories INT,
source VARCHAR(50) DEFAULT 'apple_health',
synced_at DATETIME NOT NULL,
UNIQUE KEY unique_user_date (user_id, date),
INDEX idx_steps_date (date)
)
""")
# 创建默认管理员用户(仅在用户表为空时)
cursor.execute("SELECT COUNT(*) as count FROM users")
if cursor.fetchone()["count"] == 0:
@@ -277,6 +293,74 @@ def delete_exercise(exercise_id: int):
cursor.execute("DELETE FROM exercise WHERE id = %s", (exercise_id,))
# ===== 步数记录 =====
def add_or_update_steps(user_id: int, record_date: date, steps: int, distance: float = None, calories: int = None, source: str = "apple_health") -> int:
"""添加或更新步数记录(同一天只保留一条记录)"""
with get_connection() as (conn, cursor):
cursor.execute("""
INSERT INTO steps (user_id, date, steps, distance, calories, source, synced_at)
VALUES (%s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
steps = VALUES(steps),
distance = VALUES(distance),
calories = VALUES(calories),
source = VALUES(source),
synced_at = VALUES(synced_at)
""", (
user_id,
record_date.isoformat(),
steps,
distance,
calories,
source,
datetime.now().isoformat(),
))
return cursor.lastrowid
def get_steps(start_date: Optional[date] = None, end_date: Optional[date] = None, user_id: Optional[int] = None) -> list[dict]:
"""查询步数记录"""
with get_connection() as (conn, cursor):
query = "SELECT * FROM steps WHERE 1=1"
params = []
if user_id:
query += " AND user_id = %s"
params.append(user_id)
if start_date:
query += " AND date >= %s"
params.append(start_date.isoformat())
if end_date:
query += " AND date <= %s"
params.append(end_date.isoformat())
query += " ORDER BY date DESC"
cursor.execute(query, params)
return [
{
"id": row["id"],
"date": _parse_date(row["date"]).isoformat() if isinstance(row["date"], str) else row["date"].isoformat(),
"steps": row["steps"],
"distance": row["distance"],
"calories": row["calories"],
"source": row["source"],
"synced_at": row["synced_at"].isoformat() if row["synced_at"] else None,
}
for row in cursor.fetchall()
]
def get_today_steps(user_id: int) -> dict:
"""获取今日步数"""
today = date.today()
records = get_steps(start_date=today, end_date=today, user_id=user_id)
if records:
return records[0]
return {"date": today.isoformat(), "steps": 0, "distance": None, "calories": None}
# ===== 饮食记录 =====
def add_meal(meal: Meal, user_id: int = 1) -> int:

File diff suppressed because it is too large Load Diff