feat: 用户体验优化 — 首页加载/继续记账/登录恢复

## 首页加载优化
- 分级加载:首屏只请求 overview + recentTx(2个请求)
- 数据新鲜度:30秒内不重复请求
- 次要数据异步加载:budget + todayData
- 非关键数据后台加载:userInfo + notifications

## 继续记账功能
- SaveSuccess 组件新增「继续记一笔」按钮
- 新增模式显示继续按钮,编辑模式自动返回
- 继续记账时保留分类和日期,清空金额和备注

## 登录失败恢复
- 登录重试机制:最多3次,间隔1秒
- 失败后显示提示而非静默等待

## 群组账单逻辑修正
- 群组账单 = 所有群组成员的个人账单之和
- 修改查询:WHERE user_id IN (群组成员)
This commit is contained in:
wangxiaogang
2026-06-07 17:49:46 +08:00
parent bd7af8e512
commit 1abc55323d
11 changed files with 183 additions and 87 deletions

View File

@@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS transactions (
category_id INT,
note VARCHAR(200),
date DATE NOT NULL,
group_id INT DEFAULT NULL COMMENT '群组标签NULL=纯个人记录',
group_id INT DEFAULT NULL COMMENT '群组标签(仅标记,群组账单通过 user_id 关联 group_members 统计)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_date (user_id, date),

View File

@@ -22,9 +22,9 @@ async function buildWhereClause(
)
if ((memberCheck as any[]).length === 0) return null
// 群组视图:显示该群组所有记录(包括已退出成员的历史记录)
// 群组视图:统计所有群组成员的个人账单
params.push(groupId)
let where = `WHERE t.group_id = ?`
let where = `WHERE t.user_id IN (SELECT user_id FROM group_members WHERE group_id = ?)`
if (extraConditions.length > 0) where += ' AND ' + extraConditions.join(' AND ')
return { where, params }
}

View File

@@ -15,7 +15,7 @@ function safeInt(val: any, fallback: number, min: number, max: number): number {
router.get('/:id', async (req: AuthRequest, res: Response) => {
try {
// 用户可以查看:自己的记录 + 自己所在群组的所有记录(包括已退出成员的历史记录
// 用户可以查看:自己的记录 + 自己所在群组成员的所有记录
const [rows] = await pool.query(
`SELECT t.id, t.user_id, t.amount, t.type, t.category_id, t.note, DATE_FORMAT(t.date, '%Y-%m-%d') as date, t.created_at, t.updated_at, t.group_id,
c.name as category_name, c.icon as category_icon, c.color as category_color,
@@ -25,7 +25,7 @@ router.get('/:id', async (req: AuthRequest, res: Response) => {
LEFT JOIN users u ON t.user_id = u.id
WHERE t.id = ? AND (
t.user_id = ?
OR t.group_id IN (SELECT group_id FROM group_members WHERE user_id = ?)
OR t.user_id IN (SELECT user_id FROM group_members WHERE group_id IN (SELECT group_id FROM group_members WHERE user_id = ?))
)`,
[req.params.id, req.userId, req.userId]
)
@@ -51,7 +51,7 @@ router.get('/', async (req: AuthRequest, res: Response) => {
const params: any[] = []
if (group_id && group_id !== 'null') {
// 群组视图:验证用户是群组成员,然后显示该群组所有记录(包括已退出成员的历史记录
// 群组视图:验证用户是群组成员,然后显示所有群组成员的记录
const [memberCheck] = await pool.query(
'SELECT id FROM group_members WHERE group_id = ? AND user_id = ?',
[group_id, req.userId]
@@ -59,7 +59,8 @@ router.get('/', async (req: AuthRequest, res: Response) => {
if ((memberCheck as any[]).length === 0) {
return res.status(403).json({ code: 40300, message: '无权访问此群组' })
}
where = 'WHERE t.group_id = ?'
// 群组账单 = 所有群组成员的个人账单
where = 'WHERE t.user_id IN (SELECT user_id FROM group_members WHERE group_id = ?)'
params.push(group_id)
} else {
// 个人视图:我的所有记录(不管 group_id