🔄 定期账单

Admin 管理员查看所有用户的定期记账规则(周/双周/月/年周期自动生成交易记录)。页面展示定期规则列表、频率分布、预计总金额,支持按账单/状态筛选、关键词搜索、CSV导出。

路由/recurring

页面初始化触发

1. 进入页面 → 并发请求 2 个

触发条件:组件首次挂载(useEffect 依赖为空数组)
GET /admin/recurring
✅ 页面加载时自动触发(无参数)

请求头Authorization: Bearer {token}

Query 参数(可选):

参数类型必填说明
bill_idinteger可选按账单ID筛选
is_activestring可选"true"=启用中,"false"=已停用

响应[{id, name, bill_id, bill_name, category_id, category_name, amount, tx_type, frequency, next_due_at, is_active, created_at}]

GET /admin/bills
✅ 页面加载时自动触发(并发)

请求头Authorization: Bearer {token}

Querylimit=1000

用途:填充"所属账单"下拉筛选器

筛选操作触发

2. 切换账单筛选 → GET /admin/recurring

触发条件:选择账单下拉框 → handleBillChange(val) → 更新 ref → doFetchRecurring()

3. 切换状态筛选 → GET /admin/recurring

触发条件:选择状态下拉框(启用中/已停用)→ handleStatusChange(val)

4. 清空筛选 → 重置所有 ref → 重新请求

触发条件:点击"清空"按钮(仅在有激活筛选时显示)

搜索操作

5. 输入搜索词 → 本地筛选(非请求)

触发条件:搜索框输入 → 400ms 防抖 → debouncedSearch 更新 → 本地过滤
搜索方式:纯前端本地过滤。匹配字段:名称(name)、账单名(bill_name)、类目名(category_name)。大小写不敏感。

操作按钮

6. 点击"刷新"按钮 → 保留筛选重新请求

触发条件:点击刷新按钮(loading 时禁用)

7. 点击"暂停/恢复" → PUT /admin/recurring/:id

触发条件:点击暂停/恢复按钮 → confirm() 确认 → 更新 is_active
PUT /admin/recurring/:id
{ "is_active": 0 }  // 暂停 → 0,恢复 → 1

响应: {"success": true}

8. 点击"立即执行" → POST /admin/recurring/:id/trigger

触发条件:点击立即执行按钮 → confirm() 确认 → 推进 next_due_at
POST /admin/recurring/:id/trigger

响应: {"message": "已立即执行,下次执行时间已更新", "next_due_at": "..."}

9. 点击"删除" → DELETE /admin/recurring/:id

触发条件:点击删除按钮(红色 Trash2) → confirm() 确认 → 删除整条规则
DELETE /admin/recurring/:id

响应: {"success": true}

10. 点击"批量触发" → POST /admin/recurring/advance-all

触发条件:点击批量触发按钮(按钮内实时显示当前逾期数量徽章) → confirm() 确认 → 批量推进所有 is_active=1 且已逾期规则的 next_due_at
POST /admin/recurring/advance-all

响应: {"message": "已处理 N 条逾期规则", "items": [{id, name, next_due_at}, ...]}

  • 无逾期规则时提示"暂无逾期规则需要处理"
  • advancingAll 状态防止重复点击
触发条件:点击刷新按钮(loading 时禁用)

7. 点击"导出 CSV" → 客户端生成并下载

触发条件:点击"导出 CSV"按钮
导出方式:纯前端实现。根据当前 filteredRecurring 生成 CSV,包含字段:名称、账单、类目、金额、类型、频率、下次执行、状态、创建时间。频率字段映射:daily→每天、weekly→每周、bi-weekly→双周、monthly→每月、yearly→每年。

统计卡片(纯前端计算)

  • 定期规则recurring.length
  • 启用中recurring.filter(r => r.is_active === 1).length
  • 已逾期recurring.filter(r => new Date(r.next_due_at) < new Date()).length
  • 每月预估recurring.reduce((sum, r) => sum + r.amount, 0)

表格底部合计行

!loading && filteredRecurring.length > 0 时,显示合计行:合计金额 = filteredRecurring.reduce((s, r) => s + (r.amount || 0), 0)

频率映射

显示
daily每天
weekly每周
bi-weekly双周
monthly每月
yearly每年