Compare commits
1
Commits
android-1.0
...
dev-0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5622e4b3d4 |
@@ -122,11 +122,15 @@ router.get('/habits', async (req, res, next) => {
|
||||
const userId = req.user.sub
|
||||
const today = toDateStr()
|
||||
const date = normDate(req.query.date)
|
||||
const habits = await loadActiveHabits(userId)
|
||||
const habits = (await loadActiveHabits(userId))
|
||||
// 回看历史日期时, 当天还不存在的打卡项不应出现 (与 /calendar 口径一致)
|
||||
.filter((h) => toDateStr(h.createdAt) <= date)
|
||||
const ranges = habits.map((h) => periodRange(h.period, date))
|
||||
const grouped = await loadRecords(userId, habits, ranges.length ? ranges : [{ start: date, end: date }])
|
||||
const items = habits.map((h) => buildItem(h, grouped.get(h.id) || new Map(), date, today))
|
||||
const doneCount = items.filter((i) => i.completed).length
|
||||
// summary 是"当天进度": 按当日份额 (countOnDate >= expectedPerDay) 统计,
|
||||
// 不能用 completed (周期完成度), 否则历史日期会把同周期后续日期的打卡算进来。
|
||||
const doneCount = items.filter((i) => i.dayDone).length
|
||||
res.json({ today, date, items, summary: { total: items.length, done: doneCount } })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
@@ -291,7 +295,14 @@ router.get('/calendar', async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.sub
|
||||
const today = toDateStr()
|
||||
const month = MONTH_RE.test(String(req.query.month || '')) ? String(req.query.month) : today.slice(0, 7)
|
||||
// 兼容旧版客户端: addMonths 曾把 "YYYY-MM" 算成 "YYYY-MM-01", 请求月份带了日号,
|
||||
// 旧逻辑判为非法后静默回退到本月, 导致切到上月时看不到任何记录。这里截回月份。
|
||||
const rawMonth = String(req.query.month || '')
|
||||
const month = MONTH_RE.test(rawMonth)
|
||||
? rawMonth
|
||||
: DATE_RE.test(rawMonth)
|
||||
? rawMonth.slice(0, 7)
|
||||
: today.slice(0, 7)
|
||||
const start = month + '-01'
|
||||
const end = addDays(monthStartNext(month), -1)
|
||||
const habits = await prisma.habit.findMany({ where: { userId }, orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] })
|
||||
|
||||
@@ -130,7 +130,14 @@ export default function MergedView({ date, onDateChange }) {
|
||||
}
|
||||
}
|
||||
|
||||
const summary = detail?.summary || { total: 0, done: 0 }
|
||||
// 当日进度 = 当天已打卡项 / 当天打卡项. 用 dayDone 而非 completed:
|
||||
// completed 是"周期目标已达成" (每周/每月项可能在本周期其他天完成), 放到
|
||||
// 历史日期的标题上会把当天之后、同周期内的打卡也算进来。按 items 现算还能
|
||||
// 让乐观更新 (打卡/撤销) 立即反映到标题, 不必等 /habits 重新拉取。
|
||||
const summary = useMemo(
|
||||
() => ({ total: items.length, done: items.filter((i) => i.dayDone).length }),
|
||||
[items],
|
||||
)
|
||||
const fireBurst = (id) => setBursts((m) => ({ ...m, [id]: (m[id] || 0) + 1 }))
|
||||
|
||||
// ---------- 打卡 / 撤销 (乐观更新) ----------
|
||||
@@ -256,7 +263,7 @@ export default function MergedView({ date, onDateChange }) {
|
||||
<span className="arrow" onClick={() => setMonth((m) => addMonths(m, -1))}>‹</span>
|
||||
<b>{fmtMonth(month)}</b>
|
||||
<span className="arrow" onClick={() => setMonth((m) => addMonths(m, 1))}>›</span>
|
||||
<span className="today" onClick={() => onDateChange(today)}>回到今天</span>
|
||||
<span className="today" onClick={() => { setMonth(today.slice(0, 7)); onDateChange(today) }}>回到今天</span>
|
||||
</div>
|
||||
|
||||
<div className="mv-grid mv-wd">
|
||||
|
||||
@@ -25,13 +25,22 @@ export function addDays(s, n) {
|
||||
return fromUTC(d)
|
||||
}
|
||||
|
||||
/**
|
||||
* 月份加减. 同时接受月粒度 "YYYY-MM" 与日粒度 "YYYY-MM-DD":
|
||||
* - 月粒度必须原样返回 "YYYY-MM", 不能变成 "YYYY-MM-01" —— 否则
|
||||
* `/checkin/calendar?month=2026-10-01` 会被服务端 MONTH_RE 判为非法并回退到当月,
|
||||
* 日历切到上个月时就会"看不到任何记录"。
|
||||
* - 日粒度保留原日号, 超出目标月天数时收敛到月末。
|
||||
*/
|
||||
export function addMonths(s, n) {
|
||||
const [y, m] = String(s).split('-').map(Number)
|
||||
const str = String(s)
|
||||
const [y, m, d] = str.split('-').map(Number)
|
||||
const day = d || 1
|
||||
const first = new Date(Date.UTC(y, (m || 1) - 1 + n, 1))
|
||||
const day = Number(String(s).slice(8, 10)) || 1
|
||||
const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate()
|
||||
first.setUTCDate(Math.min(day, lastDay))
|
||||
return fromUTC(first)
|
||||
const out = fromUTC(first)
|
||||
return str.length === 7 ? out.slice(0, 7) : out
|
||||
}
|
||||
|
||||
/** 周一为一周第一天 */
|
||||
|
||||
Reference in New Issue
Block a user