Files
nj/docs/opendesign/screens/desktop/home-daily.html
iven b320641d9c
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
fix(app): 全链路验证修复 — 编译错误/CORS/迁移/启动脚本
前端修复:
- calendar_page: 移除不存在的 JournalEntry.content getter
- responsive_scaffold: 移除不存在的 notchThickness 参数
- splash_page: SingleTickerProvider → TickerProvider (多 AnimationController)
- profile_page: UserRoleType.name → .code (修复运行时崩溃)
- 导入缺失的 user.dart

后端修复:
- class_service: generate_class_code 取 UUID 后6位(随机部分)避免碰撞
- diary_role_seed: 移除不存在的 id 列,使用复合主键 ON CONFLICT

基础设施:
- config/default.toml: CORS 改为通配符(开发模式)
- scripts/dev.sh: 统一启动脚本(自动清理端口)
- docs/opendesign/: Open Design 设计规格 HTML 原型稿

验证结果: flutter analyze 0 error, cargo test 77/77 通过, 17个页面全部渲染正常
2026-06-02 01:03:58 +08:00

596 lines
20 KiB
HTML
Raw 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=1440, height=900, initial-scale=1">
<title>暖记 — 桌面端首页</title>
<link rel="stylesheet" href="../css/tokens.css">
<link rel="stylesheet" href="../css/components.css">
<link rel="stylesheet" href="shared.css">
<style>
body {
width: 1440px;
height: 900px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
.main-content { height: 900px; padding-top: 32px; }
.content-inner {
padding: var(--space-6) var(--space-10);
max-width: none;
}
/* Greeting row */
.greeting-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: var(--space-6);
}
.greeting-date {
font-size: var(--text-sm);
color: var(--muted);
font-weight: 500;
margin-bottom: var(--space-1);
}
.greeting-text {
font-family: var(--font-display);
font-size: var(--text-4xl);
font-weight: 700;
color: var(--fg);
}
.greeting-text span { color: var(--accent); }
.streak-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: var(--tertiary-soft);
border-radius: var(--radius-pill);
font-size: var(--text-sm);
font-weight: 600;
color: #B8860B;
}
/* Three column layout */
.home-grid {
display: grid;
grid-template-columns: 1fr 380px;
gap: var(--space-6);
}
/* Left column: mood + today + entries */
.left-col {}
.mood-section {
background: var(--surface);
border-radius: var(--radius-md);
padding: var(--space-5);
margin-bottom: var(--space-5);
box-shadow: var(--elev-soft);
border: 1px solid var(--border-soft);
}
.mood-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-4);
}
.mood-header span {
font-size: var(--text-md);
font-weight: 600;
color: var(--fg-2);
}
.mood-header .weather {
font-size: var(--text-sm);
color: var(--muted);
display: flex;
align-items: center;
gap: 4px;
}
.mood-options {
display: flex;
justify-content: space-between;
max-width: 500px;
}
.mood-opt {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
background: none;
border: none;
cursor: pointer;
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-sm);
transition: all var(--motion-fast) var(--ease-bounce);
}
.mood-opt:hover { background: var(--surface-warm); }
.mood-opt.selected { background: var(--surface-warm); }
.mood-opt .face { font-size: 32px; }
.mood-opt .label { font-size: var(--text-xs); color: var(--muted); }
.mood-opt.selected .label { color: var(--accent); font-weight: 600; }
.today-card {
background: linear-gradient(135deg, var(--accent) 0%, var(--tertiary) 100%);
border-radius: var(--radius-lg);
padding: var(--space-6);
margin-bottom: var(--space-5);
position: relative;
overflow: hidden;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
}
.today-card:hover { transform: translateY(-2px); transition: transform var(--motion-fast) var(--ease-bounce); }
.today-card::before {
content: '';
position: absolute;
top: -30px;
right: -30px;
width: 160px;
height: 160px;
border-radius: 50%;
background: rgba(255,255,255,0.12);
}
.today-card .label {
font-size: var(--text-sm);
color: var(--accent-on);
font-weight: 500;
margin-bottom: var(--space-2);
opacity: 0.85;
}
.today-card .title {
font-family: var(--font-display);
font-size: var(--text-xl);
font-weight: 700;
color: var(--accent-on);
margin-bottom: var(--space-1);
}
.today-card .prompt {
font-size: var(--text-sm);
color: var(--accent-on);
opacity: 0.7;
}
.today-card .write-btn {
width: 56px;
height: 56px;
border-radius: 50%;
background: white;
border: none;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 14px rgba(0,0,0,0.1);
cursor: pointer;
transition: transform var(--motion-fast) var(--ease-bounce);
flex-shrink: 0;
}
.today-card .write-btn:hover { transform: scale(1.1); }
.today-card .write-btn svg { width: 24px; height: 24px; color: var(--accent); }
/* Entries grid — 3 columns */
.recent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-4);
}
.recent-header h3 {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 700;
}
.recent-header a {
font-size: var(--text-sm);
color: var(--accent);
text-decoration: none;
font-weight: 500;
}
.entries-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-4);
}
.entry-card {
background: var(--surface);
border-radius: var(--radius-md);
padding: var(--space-4);
box-shadow: var(--elev-soft);
border: 1px solid var(--border-soft);
cursor: pointer;
transition: transform var(--motion-fast) var(--ease-standard);
}
.entry-card:hover { transform: translateY(-2px); }
.entry-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--space-2);
}
.entry-date { font-size: var(--text-xs); color: var(--muted); }
.entry-mood { font-size: 18px; }
.entry-title {
font-family: var(--font-display);
font-size: var(--text-md);
font-weight: 600;
color: var(--fg);
margin-bottom: var(--space-1);
}
.entry-excerpt {
font-size: var(--text-sm);
color: var(--muted);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.5;
}
.entry-preview-thumb {
width: 100%;
height: 72px;
border-radius: var(--radius-sm);
margin-top: var(--space-3);
background: var(--surface-warm);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
/* Right column: stats + mood chart */
.right-col {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.stat-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.stat-card {
background: var(--surface);
border-radius: var(--radius-md);
padding: var(--space-5);
box-shadow: var(--elev-soft);
border: 1px solid var(--border-soft);
}
.stat-card .num {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 700;
}
.stat-card .num.accent { color: var(--accent); }
.stat-card .num.green { color: var(--secondary); }
.stat-card .label {
font-size: var(--text-sm);
color: var(--muted);
margin-top: 4px;
}
/* Mood chart */
.mood-chart-card {
background: var(--surface);
border-radius: var(--radius-md);
padding: var(--space-5);
box-shadow: var(--elev-soft);
border: 1px solid var(--border-soft);
}
.mood-chart-card h4 {
font-family: var(--font-display);
font-size: var(--text-base);
font-weight: 600;
margin-bottom: var(--space-4);
}
.chart-period {
display: flex;
gap: var(--space-2);
margin-bottom: var(--space-4);
}
.period-btn {
padding: 6px 16px;
border-radius: var(--radius-pill);
border: none;
font-size: var(--text-sm);
font-weight: 500;
background: var(--surface-warm);
color: var(--fg-2);
cursor: pointer;
}
.period-btn.active { background: var(--accent); color: var(--accent-on); }
.mood-line-chart {
height: 100px;
display: flex;
align-items: flex-end;
gap: 4px;
padding-bottom: 24px;
position: relative;
}
.mood-line-chart::after {
content: '';
position: absolute;
bottom: 20px;
left: 0;
right: 0;
height: 1px;
background: var(--border-soft);
}
.chart-bar {
flex: 1;
border-radius: 6px 6px 0 0;
position: relative;
cursor: pointer;
transition: height var(--motion-base) var(--ease-bounce);
}
.chart-bar:hover { opacity: 0.8; }
.chart-date {
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
font-size: 10px;
color: var(--meta);
white-space: nowrap;
}
/* Insight mini card */
.insight-mini {
background: var(--surface);
border-radius: var(--radius-md);
padding: var(--space-5);
box-shadow: var(--elev-soft);
border: 1px solid var(--border-soft);
}
.insight-mini h4 {
font-family: var(--font-display);
font-size: var(--text-base);
font-weight: 600;
margin-bottom: var(--space-3);
}
.insight-row {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) 0;
}
.insight-row .icon {
font-size: 20px;
}
.insight-row .text {
flex: 1;
font-size: var(--text-sm);
color: var(--fg-2);
}
.insight-row .badge {
padding: 4px 10px;
border-radius: var(--radius-pill);
font-size: 11px;
font-weight: 600;
background: var(--secondary-soft);
color: #2D7D46;
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.anim-fade { animation: fadeIn 0.5s var(--ease-standard) both; }
</style>
</head>
<body>
<!-- Status bar -->
<div class="desktop-statusbar">
<div class="traffic-lights">
<div class="dot" style="background:#FF5F57"></div>
<div class="dot" style="background:#FEBC2E"></div>
<div class="dot" style="background:#28C840"></div>
</div>
<span>暖记 Warm Notes</span>
<span style="font-variant-numeric:tabular-nums">14:32</span>
</div>
<!-- Sidebar -->
<nav class="sidebar" role="navigation" aria-label="主导航">
<div class="sidebar-brand">
<div class="sidebar-logo">📖</div>
<div>
<div class="sidebar-brand-text">暖记</div>
<div class="sidebar-brand-sub">Warm Notes</div>
</div>
</div>
<div class="sidebar-nav">
<button class="sidebar-nav-item active" aria-label="首页">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
首页
</button>
<button class="sidebar-nav-item" aria-label="日历">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
日历
</button>
<button class="sidebar-nav-item" aria-label="心情追踪">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>
心情追踪
</button>
<button class="sidebar-nav-item" aria-label="贴纸库">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="18" height="18" rx="3"/><circle cx="9" cy="10" r="1.5" fill="currentColor"/><circle cx="15" cy="10" r="1.5" fill="currentColor"/><path d="M9 15c.8.8 2.2 1.2 3 1.2s2.2-.4 3-1.2"/></svg>
贴纸库
</button>
<button class="sidebar-nav-item" aria-label="模板">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
模板
</button>
<button class="sidebar-nav-item" aria-label="发现">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
发现
</button>
</div>
<button class="sidebar-write-btn" aria-label="写日记">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
写日记
</button>
<div class="sidebar-footer">
<div class="sidebar-avatar">🐱</div>
<div>
<div class="sidebar-user-name">小暖</div>
<div class="sidebar-user-streak">连续记录 12 天</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="main-content" role="main">
<!-- Top bar -->
<div class="desktop-topbar">
<div class="desktop-topbar-title">首页</div>
<div class="desktop-topbar-actions">
<button class="topbar-action-btn" aria-label="搜索">
<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><line x1="12.5" y1="12.5" x2="16" y2="16"/></svg>
</button>
<button class="topbar-action-btn" aria-label="菜单">
<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 6h10M4 9h10M4 12h7"/></svg>
</button>
</div>
</div>
<div class="content-inner">
<!-- Greeting -->
<div class="greeting-row anim-fade">
<div>
<div class="greeting-date">2026年5月31日 · 星期日</div>
<div class="greeting-text">下午好,<span>小暖</span></div>
</div>
<div class="streak-badge">
<svg width="16" height="16" viewBox="0 0 16 16" fill="#B8860B"><path d="M8 1l2 5h5l-4 3 1.5 5L8 11 3.5 14 5 9 1 6h5z"/></svg>
连续记录 12 天
</div>
</div>
<!-- Two-column grid -->
<div class="home-grid anim-fade" style="animation-delay:0.1s">
<div class="left-col">
<!-- Mood -->
<div class="mood-section">
<div class="mood-header">
<span>今天心情如何?</span>
<div class="weather">
<svg width="16" height="16" viewBox="0 0 16 16" fill="var(--tertiary)"><circle cx="8" cy="8" r="5"/></svg>
晴 26°
</div>
</div>
<div class="mood-options">
<button class="mood-opt" aria-label="开心"><span class="face">😊</span><span class="label">开心</span></button>
<button class="mood-opt" aria-label="平静"><span class="face">😌</span><span class="label">平静</span></button>
<button class="mood-opt selected" aria-label="幸福"><span class="face">🥰</span><span class="label">幸福</span></button>
<button class="mood-opt" aria-label="疲惫"><span class="face">😴</span><span class="label">疲惫</span></button>
<button class="mood-opt" aria-label="低落"><span class="face">😔</span><span class="label">低落</span></button>
</div>
</div>
<!-- Today card -->
<div class="today-card">
<div>
<div class="label">今天的日记</div>
<div class="title">写点什么吧...</div>
<div class="prompt">记录一个温暖的瞬间,或者今天发生的小事</div>
</div>
<button class="write-btn" aria-label="开始写日记">
<svg viewBox="0 0 24 24" fill="none"><path d="M11 5v12M5 11h12" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
</button>
</div>
<!-- Recent entries -->
<div class="recent-header">
<h3>最近记录</h3>
<a href="#">查看全部</a>
</div>
<div class="entries-grid">
<div class="entry-card">
<div class="entry-top"><div class="entry-date">5月30日 · 周六</div><div class="entry-mood">😊</div></div>
<div class="entry-title">图书馆的午后</div>
<div class="entry-excerpt">今天在图书馆自习,窗外的阳光洒进来,暖暖的。复习了高数第三章...</div>
<div class="entry-preview-thumb" style="background:var(--surface-warm)">🌸</div>
</div>
<div class="entry-card">
<div class="entry-top"><div class="entry-date">5月29日 · 周五</div><div class="entry-mood">🥰</div></div>
<div class="entry-title">和舍友的火锅局</div>
<div class="entry-excerpt">考完试和舍友们去吃了火锅庆祝,大家都好开心...</div>
<div class="entry-preview-thumb" style="background:var(--secondary-soft)">🍃</div>
</div>
<div class="entry-card">
<div class="entry-top"><div class="entry-date">5月28日 · 周四</div><div class="entry-mood">😌</div></div>
<div class="entry-title">期末考试第一天</div>
<div class="entry-excerpt">终于考完了英语,感觉发挥还可以。明天继续加油!</div>
<div class="entry-preview-thumb" style="background:var(--tertiary-soft)">📝</div>
</div>
<div class="entry-card">
<div class="entry-top"><div class="entry-date">5月27日 · 周三</div><div class="entry-mood">😌</div></div>
<div class="entry-title">夜跑打卡</div>
<div class="entry-excerpt">晚上去操场跑了三圈,吹着晚风特别舒服...</div>
<div class="entry-preview-thumb" style="background:var(--rose-soft)">🌙</div>
</div>
</div>
</div>
<!-- Right column -->
<div class="right-col">
<div class="stat-cards">
<div class="stat-card"><div class="num accent">28</div><div class="label">本月日记</div></div>
<div class="stat-card"><div class="num green">12</div><div class="label">连续天数</div></div>
<div class="stat-card"><div class="num">156</div><div class="label">总日记数</div></div>
<div class="stat-card"><div class="num" style="color:var(--rose)">72%</div><div class="label">好心情占比</div></div>
</div>
<div class="mood-chart-card">
<h4>心情趋势</h4>
<div class="chart-period">
<button class="period-btn active">近7天</button>
<button class="period-btn">近30天</button>
<button class="period-btn">近3月</button>
</div>
<div class="mood-line-chart">
<div class="chart-bar" style="height:70%;background:var(--secondary)"><span class="chart-date">25日</span></div>
<div class="chart-bar" style="height:50%;background:var(--tertiary)"><span class="chart-date">26日</span></div>
<div class="chart-bar" style="height:85%;background:var(--accent)"><span class="chart-date">27日</span></div>
<div class="chart-bar" style="height:75%;background:var(--secondary)"><span class="chart-date">28日</span></div>
<div class="chart-bar" style="height:90%;background:var(--accent)"><span class="chart-date">29日</span></div>
<div class="chart-bar" style="height:65%;background:var(--secondary)"><span class="chart-date">30日</span></div>
<div class="chart-bar" style="height:88%;background:var(--accent)"><span class="chart-date">31日</span></div>
</div>
</div>
<div class="insight-mini">
<h4>心情洞察</h4>
<div class="insight-row">
<span class="icon">🌟</span>
<span class="text">最佳心情日5月14日</span>
<span class="badge">开心</span>
</div>
<div class="insight-row">
<span class="icon">💡</span>
<span class="text">好心情常与「朋友」「美食」相关</span>
</div>
<div class="insight-row">
<span class="icon">📈</span>
<span class="text">心情较上月提升</span>
<span class="badge">+15%</span>
</div>
</div>
</div>
</div>
</div>
</main>
<script src="../../js/theme-switcher.js"></script>
</body>
</html>