diff --git a/docs/design-system.md b/docs/design-system.md
new file mode 100644
index 0000000..24b1dd3
--- /dev/null
+++ b/docs/design-system.md
@@ -0,0 +1,396 @@
+# 戒烟助手 - 设计语言文档
+
+> 所有页面和组件必须遵循此文档,确保视觉一致性。
+
+---
+
+## 1. 色彩体系
+
+### 品牌色 — 薄荷绿
+
+| 用途 | 色值 | 示例场景 |
+|------|------|---------|
+| 主色 | `#34C8A0` | 按钮、进度条、选中态边框 |
+| 主色深 | `#1a8c62` | 选中文字、强调文字 |
+| 主色最深 | `#0D3D2E` | 页面标题、数值文字 |
+| 辅助文字 | `#52806E` | 副标题 |
+| 次要文字 | `#7aA898` | 说明文字、标签、单位 |
+| 淡底色 | `#9CC5B5` | placeholder 文字 |
+
+### 功能色
+
+| 用途 | 色值 | 场景 |
+|------|------|------|
+| 成功/正向 | `#1a8c62` | 下降趋势、达成状态 |
+| 警告 | `#B45309` | 上升趋势、需注意 |
+| 警告背景 | `rgba(251, 191, 36, 0.14)` | 警告标签底色 |
+| 危险 | `#DC2626` | 高数值指标 |
+| 信息蓝 | `#2563EB` | 对比数据(实际值) |
+
+### 背景与表面
+
+| 用途 | 色值 |
+|------|------|
+| 页面背景渐变 | `linear-gradient(180deg, #E6F7F2 0%, #F0FBF7 40%, #FAFFFE 100%)` |
+| 卡片底色 | `rgba(255, 255, 255, 0.88)` |
+| 卡片边框 | `rgba(52, 200, 160, 0.14)` |
+| 卡片阴影 | `0 4rpx 18rpx rgba(52, 200, 160, 0.07)` |
+| 浅底色块 | `rgba(52, 200, 160, 0.06)` |
+| 选中底色 | `rgba(52, 200, 160, 0.09)` |
+| 选中边框 | `rgba(52, 200, 160, 0.45)` |
+
+---
+
+## 2. 字体规范
+
+### 字体栈
+
+```css
+font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+```
+
+### 字号层级
+
+| 级别 | 字号 | 字重 | 颜色 | 用途 |
+|------|------|------|------|------|
+| H1 | 38rpx | 700 | `#0D3D2E` | 页面大标题 |
+| H2 | 28rpx | 600 | `#0D3D2E` | 卡片标题 |
+| H3 | 26rpx | 600 | `#1a5c45` | 区块标签(section-label) |
+| 数值大 | 52rpx | 700 | `#0D3D2E` | 核心指标数字 |
+| 数值中 | 38-42rpx | 700 | `#0D3D2E` | 次要指标数字 |
+| 正文 | 25rpx | 400 | `#52806E` | 描述文字 |
+| 辅助 | 21-22rpx | 400 | `#7aA898` | 标签、单位、说明 |
+| 小字 | 20rpx | 500 | `#7aA898` | 注释、补充信息 |
+
+---
+
+## 3. 间距与圆角
+
+### 间距
+
+| 级别 | 数值 | 用途 |
+|------|------|------|
+| xs | 6-8rpx | 元素内部微间距 |
+| sm | 14-16rpx | 标签之间、紧凑元素间距 |
+| md | 20rpx | 卡片之间、区块之间 |
+| lg | 28rpx | 页面侧边距 |
+
+### 圆角
+
+| 元素 | 圆角 |
+|------|------|
+| 卡片 | 24rpx |
+| 选项卡片 | 20rpx |
+| 标签(pill) | 999rpx |
+| 按钮 | 48rpx(大按钮)/ 999rpx(小标签) |
+| 输入框/选择器 | 12-14rpx |
+| 圆形按钮/圆环 | 50% |
+| 数据色块 | 14rpx |
+
+---
+
+## 4. 组件样式规范
+
+### 4.1 卡片(Card)
+
+所有独立内容区域使用卡片包裹。
+
+```css
+.card {
+ background: rgba(255, 255, 255, 0.88);
+ border-radius: 24rpx;
+ border: 1.5rpx solid rgba(52, 200, 160, 0.14);
+ box-shadow: 0 4rpx 18rpx rgba(52, 200, 160, 0.07);
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+}
+```
+
+**禁止**:使用旧版灰蓝阴影 `rgba(15, 23, 42, 0.06)`,统一使用薄荷绿阴影。
+
+### 4.2 选择卡片(Mode Card / Option Card)
+
+```css
+/* 默认态 */
+.option-card {
+ background: rgba(255, 255, 255, 0.72);
+ border: 2rpx solid rgba(52, 200, 160, 0.1);
+ border-radius: 20rpx;
+ box-shadow: 0 2rpx 10rpx rgba(52, 200, 160, 0.04);
+}
+
+/* 选中态 */
+.option-card-active {
+ background: rgba(52, 200, 160, 0.09);
+ border-color: rgba(52, 200, 160, 0.45);
+ box-shadow: 0 4rpx 16rpx rgba(52, 200, 160, 0.14);
+}
+```
+
+### 4.3 标签选项(Pill Tag)
+
+```css
+/* 默认态 */
+.tag {
+ padding: 14rpx 26rpx;
+ border-radius: 999rpx;
+ background: rgba(255, 255, 255, 0.9);
+ font-size: 25rpx;
+ color: #4a7a66;
+ border: 1.5rpx solid rgba(52, 200, 160, 0.18);
+}
+
+/* 选中态 */
+.tag-active {
+ background: rgba(52, 200, 160, 0.12);
+ border-color: rgba(52, 200, 160, 0.45);
+ color: #1a7f61;
+ font-weight: 600;
+}
+```
+
+### 4.4 按钮
+
+```css
+/* 主按钮 */
+.btn-primary {
+ height: 96rpx;
+ background: linear-gradient(180deg, #3DD9AE 0%, #34C8A0 100%);
+ border-radius: 48rpx;
+ color: #FFFFFF;
+ font-size: 32rpx;
+ font-weight: 600;
+ box-shadow: 0 12rpx 28rpx rgba(52, 200, 160, 0.28);
+}
+
+/* 圆形操作按钮(+/-) */
+.btn-circle {
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ background: rgba(52, 200, 160, 0.1);
+ color: #34C8A0;
+ font-size: 38rpx;
+ border: 1.5rpx solid rgba(52, 200, 160, 0.25);
+}
+```
+
+### 4.5 输入框
+
+```css
+.input {
+ background: rgba(52, 200, 160, 0.06);
+ border: 1.5rpx solid rgba(52, 200, 160, 0.2);
+ border-radius: 12rpx;
+ color: #0D3D2E;
+ font-weight: 600;
+}
+
+/* placeholder */
+placeholder-style: "color: #9CC5B5"
+```
+
+### 4.6 进度条
+
+```css
+.progress-bar {
+ height: 10rpx;
+ background: rgba(52, 200, 160, 0.1);
+ border-radius: 999rpx;
+}
+
+.progress-fill {
+ background: linear-gradient(90deg, #34C8A0, #3DD9AE);
+ border-radius: 999rpx;
+}
+
+/* 未完成态 */
+.progress-fill-pending {
+ background: rgba(52, 200, 160, 0.3);
+}
+```
+
+### 4.7 圆环指标(Ring)
+
+```css
+.ring {
+ width: 110rpx;
+ height: 110rpx;
+ border-radius: 50%;
+ /* 使用 conic-gradient 动态渲染 */
+ /* 主色: #34C8A0, 底色: #E8F8F3 */
+}
+
+.ring-inner {
+ width: 82rpx;
+ height: 82rpx;
+ border-radius: 50%;
+ background: #FFFFFF;
+ box-shadow: 0 4rpx 12rpx rgba(52, 200, 160, 0.1);
+}
+```
+
+### 4.8 状态标签(Chip)
+
+```css
+/* 正向 */
+.chip-good {
+ background: rgba(52, 200, 160, 0.12);
+ color: #1a8c62;
+}
+
+/* 警告 */
+.chip-warn {
+ background: rgba(251, 191, 36, 0.14);
+ color: #B45309;
+}
+
+/* 中性 */
+.chip-neutral {
+ background: rgba(52, 200, 160, 0.06);
+ color: #7aA898;
+}
+```
+
+### 4.9 Segment(Tab 切换)
+
+```css
+.segment {
+ background: rgba(255, 255, 255, 0.82);
+ padding: 6rpx;
+ border-radius: 22rpx;
+ border: 1.5rpx solid rgba(52, 200, 160, 0.14);
+ box-shadow: 0 4rpx 16rpx rgba(52, 200, 160, 0.07);
+}
+
+.segment-item {
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #7aA898;
+ border-radius: 16rpx;
+}
+
+.segment-active {
+ background: #FFFFFF;
+ color: #0D3D2E;
+ box-shadow: 0 4rpx 12rpx rgba(52, 200, 160, 0.12);
+}
+```
+
+### 4.10 空状态
+
+```css
+.empty-block {
+ padding: 32rpx;
+ border-radius: 16rpx;
+ background: rgba(52, 200, 160, 0.04);
+}
+
+/* 虚线边框变体 */
+.empty-block-dashed {
+ border: 2rpx dashed rgba(52, 200, 160, 0.2);
+ background: transparent;
+}
+
+.empty-text {
+ font-size: 24rpx;
+ color: #7aA898;
+}
+```
+
+### 4.11 底部安全区
+
+```css
+.bottom-safe {
+ height: calc(32rpx + env(safe-area-inset-bottom));
+}
+
+/* 固定底部按钮区域 */
+.footer-fixed {
+ position: fixed;
+ left: 0; right: 0; bottom: 0;
+ padding: 20rpx 28rpx;
+ padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+ background: linear-gradient(180deg, transparent 0%, rgba(240, 251, 247, 0.97) 35%);
+}
+```
+
+---
+
+## 5. 数据可视化配色
+
+| 数据类型 | 色值/色阶 |
+|---------|----------|
+| 0 / 无数据 | `rgba(52, 200, 160, 0.06)` |
+| 低(1-3) | `rgba(52, 200, 160, 0.18)` |
+| 中(4-8) | `rgba(251, 191, 36, 0.2)` |
+| 高(9+) | `rgba(239, 68, 68, 0.15)` |
+| 圆环已完成 | `#34C8A0` |
+| 圆环未完成 | `#E8F8F3` |
+
+---
+
+## 6. 禁止事项
+
+| 禁止 | 替代方案 |
+|------|---------|
+| 灰蓝色阴影 `rgba(15, 23, 42, *)` | 薄荷绿阴影 `rgba(52, 200, 160, *)` |
+| 旧主色 `#1AA37A` / `#1aa37a` | 新主色 `#34C8A0`(按钮/进度条)或 `#1a8c62`(文字) |
+| 灰色文字 `#6B7280` / `#94A3B8` | 绿调文字 `#7aA898` / `#52806E` |
+| 灰色背景 `#F1F5F9` / `#F8FAFC` | 绿调背景 `rgba(52, 200, 160, 0.06)` |
+| 纯白卡片背景 `#FFFFFF` | 半透明白 `rgba(255, 255, 255, 0.88)` |
+| `backdrop-filter: blur()` | 不再使用模糊滤镜(性能考虑),用不透明度代替 |
+| 旧页面背景渐变 `#edf2f8 → #fbfdff` | 薄荷绿渐变 `#E6F7F2 → #FAFFFE` |
+
+---
+
+## 7. 快速参考
+
+开发新页面时复制此基础结构:
+
+```vue
+
+
+
+
+
+ 标题
+
+
+
+
+
+
+
+
+```
diff --git a/src/api/auth.js b/src/api/auth.js
index 0c6608b..325fa99 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -1,14 +1,17 @@
import { request } from './request'
import { MINI_PROGRAM_ID } from '@/config'
-import { storage, SESSION_KEY, USER_KEY } from '@/utils/storage'
+import pinia, { useUserStore } from '@/stores'
+import { storage, SESSION_KEY, USER_KEY, USER_MODE_KEY } from '@/utils/storage'
-const H5_DEBUG_SESSION_KEY = 'o3dUk5QYaPdfMN9hBxeuouE0q63E'
+const H5_DEBUG_SESSION_KEY = 'FxLFPHHBw49loODmRSvqdg=='
export function applyH5DebugSession() {
let applied = false
// #ifdef H5
- if (process.env.NODE_ENV === 'development' && !storage.get(SESSION_KEY)) {
+ if (process.env.NODE_ENV === 'development' && storage.get(SESSION_KEY) !== H5_DEBUG_SESSION_KEY) {
storage.set(SESSION_KEY, H5_DEBUG_SESSION_KEY)
+ storage.remove(USER_KEY)
+ storage.remove(USER_MODE_KEY)
applied = true
}
// #endif
@@ -25,9 +28,11 @@ export async function login() {
mini_program_id: MINI_PROGRAM_ID,
code: loginRes.code
})
-
- storage.set(SESSION_KEY, res.data.session_key)
- storage.set(USER_KEY, res.data.user)
+ const userStore = useUserStore(pinia)
+ userStore.setUser(res.data.user, res.data.session_key)
+ if (res.data.user?.mode) {
+ userStore.setMode(res.data.user.mode)
+ }
resolve(res.data)
} catch (e) {
diff --git a/src/api/request.js b/src/api/request.js
index 4c0e420..23898ca 100644
--- a/src/api/request.js
+++ b/src/api/request.js
@@ -13,6 +13,7 @@ function isInvalidToken(res) {
export const request = {
async request(options) {
const sessionKey = storage.get(SESSION_KEY)
+ // 测试
const isRetryAfter401 = options._retryAfter401 === true
return new Promise((resolve, reject) => {
diff --git a/src/components/smoke-record-dialog/smoke-record-dialog.vue b/src/components/smoke-record-dialog/smoke-record-dialog.vue
index 4546051..2e242bb 100644
--- a/src/components/smoke-record-dialog/smoke-record-dialog.vue
+++ b/src/components/smoke-record-dialog/smoke-record-dialog.vue
@@ -262,10 +262,11 @@ export default {
overflow: hidden;
transform: translateY(100%);
transition: transform 0.3s ease-out;
- padding-bottom: 16rpx;
+ padding-bottom: 0;
border-top: 2rpx solid rgba(255, 255, 255, 0.72);
backdrop-filter: blur(28rpx);
-webkit-backdrop-filter: blur(28rpx);
+ box-shadow: 0 20rpx 48rpx rgba(15, 23, 42, 0.18);
}
.dialog-show {
@@ -308,7 +309,8 @@ export default {
.dialog-body {
padding: 16rpx 32rpx 24rpx;
- max-height: 62vh;
+ padding-bottom: 40rpx;
+ max-height: 58vh;
overflow-y: auto;
}
@@ -538,6 +540,8 @@ export default {
.dialog-footer {
padding: 16rpx 32rpx 32rpx;
+ padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
+ background: linear-gradient(180deg, rgba(248, 250, 252, 0) 0%, rgba(248, 250, 252, 0.96) 28%);
}
.dialog-btn-primary {
diff --git a/src/pages.json b/src/pages.json
index f26ccfb..f2a4d62 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -24,6 +24,12 @@
"navigationStyle": "custom"
}
},
+ {
+ "path": "pages/stats-calendar/index",
+ "style": {
+ "navigationBarTitleText": "日历详情"
+ }
+ },
{
"path": "pages/ai/index",
"style": {
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index f476bb4..719005c 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -1,8 +1,8 @@
-
+
-
-
+
+
@@ -16,100 +16,92 @@
-
-
-
-
-
-
-
- {{ quitDays }}
- 天
-
+
+
+
+
+ 无烟旅程
+ 今天也在慢慢变好
+ {{ quitEncouragement }}
- 已坚持戒烟
+ {{ todayChecked ? '今日已完成' : '等待打卡' }}
-
-
-
- 💰
-
- ¥{{ savedMoney }}
- 已省下
+
+
+
+
+ {{ quitDays }}
+ 天
+
+ 连续无烟
-
-
- 🚬
-
- {{ avoidedCigs }}
- 少抽(支)
+
+
+
+ 已省下
+ ¥{{ savedMoney }}
-
-
-
- ⏱️
-
- {{ lifeSaved }}
- 延长(小时)
+
+ 少抽
+ {{ avoidedCigs }} 支
+
+
+ 延长生命
+ {{ lifeSaved }} 小时
-
-
-
-
-
- ✓
- +
-
+
+
+
+ ✓
+ +
-
- {{ todayChecked ? '今日已打卡' : '今天没抽烟' }}
- {{ todayChecked ? `${todayCheckinTime} 已记录` : '点击打卡,记录无烟的一天' }}
-
-
- +1
+
+ {{ todayChecked ? '今日已打卡' : '今天没抽烟' }}
+ {{ todayChecked ? `${todayCheckinTime} 已完成记录` : '点击打卡,确认自己度过了无烟的一天' }}
+
+ {{ todayChecked ? '已完成' : '去打卡' }}
+
-
-
-