MERN待办应用中每日任务的创建与定时展示实现方案咨询
MERN待办应用:重复任务与定时展示实现思路
一、模型设计:合并为单一Task模型(更易维护)
不用拆分Task和DailyTask,通过字段区分任务类型与重复规则,避免冗余:
const mongoose = require('mongoose'); const taskSchema = new mongoose.Schema({ title: { type: String, required: true }, description: String, // 任务类型:普通/重复 type: { type: String, enum: ['normal', 'repeat'], default: 'normal' }, // 重复规则(仅repeat类型生效) repeatRule: { frequency: { type: String, enum: ['daily', 'weekly'] }, // weekly类型时,存储目标星期几(0=周日,1-6=周一至周六),比如[1,3,5]代表周一、三、五 weekdays: [Number] }, // 普通任务的截止时间 / 重复任务的初始执行时间 dueDate: Date, // 重复任务的下次执行时间(核心字段,用于前端计算剩余时间) nextOccurrence: Date, // 重复任务的上次完成时间(用于计算下一次执行时间) lastCompleted: Date, completed: { type: Boolean, default: false } }); module.exports = mongoose.model('Task', taskSchema);
二、后端核心逻辑:计算与更新下次执行时间
后端负责处理重复任务的nextOccurrence计算,确保每次任务完成或创建时生成正确的下次时间:
1. 创建重复任务时初始化nextOccurrence
function initRepeatTaskNextOccurrence(task) { if (task.type !== 'repeat') return task; const now = new Date(); let nextDate = new Date(task.dueDate); // 如果初始时间已过期,直接计算下一次符合规则的时间 if (nextDate <= now) { nextDate = calculateNextOccurrence(task); } task.nextOccurrence = nextDate; return task; }
2. 完成重复任务时更新nextOccurrence
function calculateNextOccurrence(task) { const now = new Date(); let nextDate; if (task.repeatRule.frequency === 'daily') { // 每日重复:当前日期加1天 nextDate = new Date(task.nextOccurrence || task.dueDate); nextDate.setDate(nextDate.getDate() + 1); // 若加1天后仍早于当前时间,继续加(避免同一天多次完成的情况) while (nextDate <= now) { nextDate.setDate(nextDate.getDate() + 1); } } else if (task.repeatRule.frequency === 'weekly') { const targetWeekdays = task.repeatRule.weekdays.sort(); const currentDay = now.getDay(); nextDate = new Date(now); // 寻找最近的下一个目标星期几 let found = false; for (const day of targetWeekdays) { if (day > currentDay) { nextDate.setDate(now.getDate() + (day - currentDay)); found = true; break; } } // 若今天之后无目标星期几,取下周第一个目标日 if (!found) { const firstDay = targetWeekdays[0]; nextDate.setDate(now.getDate() + (7 - currentDay + firstDay)); } // 保持与初始dueDate一致的时分秒 nextDate.setHours( task.dueDate.getHours(), task.dueDate.getMinutes(), task.dueDate.getSeconds() ); } return nextDate; }
三、前端实现:剩余时间计算与定时展示
1. 剩余时间计算工具函数
// 传入目标日期(普通任务用dueDate,重复任务用nextOccurrence) function getRemainingTime(targetDate) { const now = new Date(); const diff = targetDate.getTime() - now.getTime(); if (diff <= 0) return { days: 0, hours: 0, minutes: 0, seconds: 0 }; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); return { days, hours, minutes, seconds }; }
2. 实时更新剩余时间
以React为例,用useEffect+setInterval实现每秒刷新:
import { useState, useEffect } from 'react'; function TaskItem({ task }) { const [remainingTime, setRemainingTime] = useState({}); useEffect(() => { const targetDate = task.type === 'repeat' ? task.nextOccurrence : task.dueDate; // 初始化剩余时间 setRemainingTime(getRemainingTime(new Date(targetDate))); // 每秒更新一次 const timer = setInterval(() => { setRemainingTime(getRemainingTime(new Date(targetDate))); }, 1000); return () => clearInterval(timer); }, [task]); return ( <div className="task"> <h3>{task.title}</h3> <p>剩余时间:{remainingTime.days}天 {remainingTime.hours}时 {remainingTime.minutes}分 {remainingTime.seconds}秒</p> </div> ); }
3. 定时刷新任务数据
为避免后端更新nextOccurrence后前端数据过时,可设置定时刷新(比如每小时一次),或在用户完成任务后立即重新获取任务列表:
// 定时刷新任务列表 useEffect(() => { const fetchTasks = async () => { const res = await fetch('/api/tasks'); const tasks = await res.json(); setTasks(tasks); }; fetchTasks(); const interval = setInterval(fetchTasks, 3600000); // 每小时刷新一次 return () => clearInterval(interval); }, []);
四、关键逻辑说明
- 重复任务完成后不标记为
completed: true,仅更新lastCompleted和nextOccurrence,保证后续仍会展示。 - 普通任务完成后标记
completed: true,前端可根据状态隐藏或灰化展示。 - 后端计算
nextOccurrence时,需处理“当前时间已过目标时间”的边界情况,避免出现负的剩余时间。
内容的提问来源于stack exchange,提问作者Belius
相关产品推荐
相关产品推荐

