## 首页加载优化 - 分级加载:首屏只请求 overview + recentTx(2个请求) - 数据新鲜度:30秒内不重复请求 - 次要数据异步加载:budget + todayData - 非关键数据后台加载:userInfo + notifications ## 继续记账功能 - SaveSuccess 组件新增「继续记一笔」按钮 - 新增模式显示继续按钮,编辑模式自动返回 - 继续记账时保留分类和日期,清空金额和备注 ## 登录失败恢复 - 登录重试机制:最多3次,间隔1秒 - 失败后显示提示而非静默等待 ## 群组账单逻辑修正 - 群组账单 = 所有群组成员的个人账单之和 - 修改查询:WHERE user_id IN (群组成员)
8.4 KiB
8.4 KiB
小菜记账 (Xiaocai Accounting)
可爱 × 高级的个人记账小程序。让记账从负担变成享受。
Tech Stack
前端 (client/)
- 框架: Uni-app (Vue 3 + Composition API)
- 状态管理: Pinia
- 样式: SCSS (Claymorphism 风格)
- 图标: Lucide Icons (SVG 组件,禁止 emoji)
- 图表: uCharts (小程序端)
- 目标平台: 微信小程序 / H5
后端 (server/)
- 运行时: Node.js + Express
- 数据库: MySQL 8.0 (mysql2 连接池)
- 认证: HMAC-SHA256 签名 token,30 天过期
- 文件上传: multer (头像,最大 2MB)
- 备份: Node.js mysql2 导出 (不依赖 mysqldump)
- CORS: 已配置,支持 credentials
Design System
完整设计规格: docs/superpowers/specs/2026-05-27-xiaocai-design.md
核心设计语言: Claymorphism (软3D粘土风格)
- 主色调: 珊瑚橙
#FF8C69— 温暖、活泼、有亲和力 - 背景色: 暖奶油
#FFF8F0— 比纯白更柔和 - 文字色: 暖深棕
#2D1B1B— 不用纯黑 - 圆角: 卡片 20px, 按钮 14-16px
- 阴影: 双层 (外阴影 + 内阴影,模拟粘土质感)
字体
- 英文: Fredoka (标题) + Nunito (正文)
- 中文: PingFang SC / 系统默认
- 数字和金额使用 Fredoka,等宽数字
关键原则
- 最小触摸目标 44×44px
- 禁止使用 emoji 作为图标,统一使用 Icon 组件
- 尊重 prefers-reduced-motion
- 文字对比度 ≥ 4.5:1 (WCAG AA)
小程序胶囊适配
- 微信小程序右上角有胶囊按钮,页面顶部右侧元素(图标、按钮)需避开
- 使用
system.ts导出的capsuleRight计算安全距离 - 导航栏添加动态 padding:
:style="{ paddingRight: capsuleRight + 'px' }" - 居中标题页面(统计、账单、我的)不需要处理
- 有右侧元素的页面(首页 bell、编辑页保存按钮)必须处理
Project Conventions
命名
- 目录: kebab-case (
category-manage/,transaction-item/) - 组件: PascalCase (
AmountCard.vue,TransactionItem.vue) - 变量/函数: camelCase (
totalExpense,formatAmount()) - 常量: UPPER_SNAKE_CASE (
MAX_BUDGET,STORAGE_KEY) - CSS 类: BEM 或 Tailwind 风格
文件组织
client/src/
├── pages/ # 页面 (uni-app 规范)
│ ├── index/ # 首页仪表盘
│ ├── add/ # 添加/编辑记录
│ ├── stats/ # 统计分析
│ ├── bills/ # 全部账单 (下拉刷新/上拉加载)
│ ├── profile/ # 我的(身份切换入口)
│ ├── profile-edit/ # 编辑资料(昵称+头像)
│ ├── group-manage/ # 群组管理(创建/加入/退出/解散)
│ ├── category-manage/ # 分类管理
│ └── budget/ # 预算设置
├── components/ # 公共组件
│ ├── BudgetBar/ # 预算进度条
│ ├── CategoryIcon/ # 分类图标 (首字+颜色)
│ ├── ChartWrapper/ # uCharts 图表封装
│ ├── Icon/ # 图标组件 (PNG 映射)
│ ├── Numpad/ # 数字键盘
│ ├── SaveSuccess/ # 保存成功动画
│ ├── Skeleton/ # 骨架屏
│ └── TransactionItem/ # 交易列表项
├── stores/ # Pinia stores
│ ├── transaction.ts # 交易 CRUD (自动传递 group_id)
│ ├── category.ts # 分类 CRUD
│ ├── budget.ts # 预算管理
│ ├── stats.ts # 统计概览 (自动传递 group_id)
│ ├── user.ts # 用户信息 (昵称/头像)
│ └── group.ts # 群组管理 (身份切换)
├── api/ # API 封装
│ ├── auth.ts # 登录接口
│ ├── transaction.ts # 交易接口
│ ├── category.ts # 分类接口
│ ├── budget.ts # 预算接口
│ ├── stats.ts # 统计接口
│ ├── user.ts # 用户信息接口 (含头像上传)
│ ├── group.ts # 群组接口
│ └── index.ts # barrel 导出
├── utils/
│ ├── format.ts # 金额/日期格式化
│ ├── request.ts # HTTP 请求封装 (401 自动重登录+队列重试)
│ ├── system.ts # 系统信息 (statusBarHeight, capsuleRight)
│ └── app-ready.ts # App 启动就绪机制 (waitForReady)
├── config.ts # 全局配置 (API_BASE)
└── static/icons/ # tabBar 图标 (PNG)
server/src/
├── index.ts # Express 入口 (CORS, JSON, 静态文件, 路由注册)
├── middleware/auth.ts # HMAC token 验证 (timingSafeEqual)
├── routes/
│ ├── auth.ts # 登录 / token 签发 (返回用户信息)
│ ├── transaction.ts # 交易 CRUD (支持 group_id 视图切换)
│ ├── category.ts # 分类 CRUD (默认+自定义)
│ ├── budget.ts # 预算 CRUD (按月)
│ ├── stats.ts # 统计查询 (支持 group_id)
│ ├── user.ts # 用户信息 + 头像上传
│ ├── group.ts # 群组 CRUD (创建/加入/退出/解散)
│ └── backup.ts # 备份 API
├── utils/
│ ├── backup.ts # Node.js 数据库备份 (gzip)
│ └── date.ts # 日期工具
└── db/
├── connection.ts # MySQL 连接池
├── init.ts # 建表 + seed + 迁移 (幂等)
├── schema.sql # 表结构
└── seed.sql # 默认分类数据
数据库表
users— 用户 (openid, nickname, avatar_url)categories— 分类 (user_id=0 默认, 其他=自定义)transactions— 交易记录 (user_id, group_id, amount 分)budgets— 预算 (user_id, month, amount)groups— 群组 (name, invite_code, created_by)group_members— 群组成员 (group_id, user_id, role)
金额处理
- 存储单位: 分 (整数,避免浮点精度问题)
- 显示单位: 元 (保留两位小数)
- 格式化:
¥ 1,234.56(¥ 后空格,千分位分隔) - 收入前缀
+,支出前缀- - 使用
formatAmount()带 ¥ 前缀,formatAmountRaw()纯数字
数据持久化
- 前端本地存储 Key:
xc:token— 认证 tokenxc:userId— 用户 IDxc:nickname— 用户昵称xc:avatar_url— 头像文件名xc:currentGroupId— 当前选中群组 ID (null=个人模式)
- 后端 MySQL 数据库
- API 基础地址:
config.ts中的API_BASE
认证流程
- H5: 页面加载时自动 demo-login 获取 token
- 微信小程序:
App.vueonLaunch 调用wx.login获取 code,换 token - Token: HMAC-SHA256 签名,30 天过期
- 401 处理: 请求入队 → 自动重登录 → 队列中所有请求重试
- 启动就绪:
app-ready.ts的waitForReady()确保页面在登录完成后才加载数据
一起记 (群组)
- 群组仅做数据聚合,记录始终属于记录者 (user_id)
- 个人视图:
WHERE user_id = 我(所有记录) - 群组视图:
WHERE user_id IN (群组成员)— 统计所有群组成员的个人账单 - 群组账单 = 所有群组成员的个人账单之和
- 退出群组: 该用户的 group_id 设为 NULL,记录回到个人视图
- 身份切换: 「我的」页底部弹窗选择,全局生效
Git
- 分支:
master→main(注意远程) - Commit 风格: 中文描述,简洁明了
feat: 添加记账页面fix: 修复金额计算精度问题
Commands
# 前端开发 (H5)
cd client && npm run dev:h5
# 前端开发 (微信小程序)
cd client && npm run dev:mp-weixin
# 前端构建
cd client && npm run build:mp-weixin
# 后端开发
cd server && npm run dev
# 后端构建
cd server && npm run build
# 数据库初始化
cd server && npm run db:init
部署
- 开发服务:
dev.xiaocai.j35.site:3000 - 正式服务:
xiaocai.j35.site - MySQL: 本地 3306 端口
- 小程序开发版自动连接 dev 服务,正式版/H5 连接线上 (config.ts)
手动部署步骤
# 1. 本地构建后端
cd server && npm run build
# 2. 复制 dist 目录到服务器
scp -r server/dist/* root@106.14.208.43:/var/www/xiaocai/
# 3. SSH 到服务器
ssh root@106.14.208.43
# 4. 安装依赖(如 node_modules 已存在可跳过)
cd /var/www/xiaocai && npm install --production
# 5. 重启服务
pm2 restart xiaocai-server
# 或直接运行
NODE_ENV=production node dist/index.js
开发
@DEV.md