基于番茄钟技术的微信小程序,如何开发强大日历功能?
基于番茄钟技术的微信小程序,如何开发强大日历功能?
文章浏览阅读889次,点赞18次,收藏2次。通过以上步骤,我们成功为微信小程序开发了一个功能强大的日历板块。这个日历不仅能够展示当前日期,还能与用户的任务数据紧密结合
在节奏飞快的当代生活中,如何有效利用时间是个普遍关心的问题。番茄钟作为著名的时间管理方法,通过专注和放松交替的方式,让许多人成功提高了工作成效。如果你打算开发一个运用番茄钟原理的微信小程序,那么日历模块就非常必要了。接下来,就说说怎样为这个小程序设计既简单又实用的日历功能。
获取当前日期和任务信息
用户兴致高昂地打开日历界面时,我们须立即取得当前日期和待办事项资料。借助 wx. 从本地数据储存中调取任务内容,如同守护珍宝一般,保证用户的任务资料一直妥善保管着,随时可以提取查阅。这一环节就好比为整个日历功能打下了牢固基础,使后续步骤拥有稳定支撑。
生成完整日历视图
生成完整的日历视图需要费一番功夫。必须逐天计算当月所有日期,同时还要补上前后月份的空缺日期。这样处理,即便是处于两个月份之间的日期,也能在当前月份的界面里准确呈现。试想一下,用户打开日历,看到的是井然有序又精确无误的日期布局,内心会感到十分满意,对各项事务的统筹规划也会更加一目了然。
onLoad() {
const tasks = wx.getStorageSync('tasks') || {}; // 获取缓存中的任务数据
this.getCurrentMonth(); // 获取当前月份
this.generateCalendar(); // 生成日历数据
const today = new Date();
const day = today.getDate();
const month = today.getMonth() 1; // 月份是从0开始的,需要加1
const year = today.getFullYear();
const todayDate = `${year}年${month}月${day}日`;
this.setData({
todayDate,
today: day,
month,
year,
currentYear: `${year}年`,
currentMonth: `${year}年${month}月`,
tasks,
});
this.updateCalendar(); // 更新日历数据
this.setSelectedPeriod(0); // 默认选中周期
},
选择日期并显示任务
generateCalendar() {
const daysInMonth = new Date(this.data.year, this.data.month, 0).getDate(); // 获取该月天数
const firstDayOfMonth = new Date(this.data.year, this.data.month - 1, 1).getDay(); // 获取该月第一天是星期几
const days = [];
for (let i = 0; i < firstDayOfMonth; i ) {
days.push(null); // 填充前面的空白
}
for (let i = 1; i <= daysInMonth; i ) {
days.push(i); // 添加每一天的日期
}
this.setData({
days,
});
},
用户充满希望地选定某个时间,接下来就要呈现这个时间点的相关事项。依据选定的时刻,从清单中找出匹配的事项并呈现。这就如同为用户推开了一道通往特定时间事项的门扉,使他们能够明确了解当天有哪些未完成的工作,从而提前安排。
支持7天为周期的日期展示
setSelectedDate(e) {
const selectedDate = e.target.dataset.date;
const dayInfo = this.data.tasks[selectedDate] || "没有任务安排";
this.setData({
selectedDate,
dayInfo,
});
},
为了方便用户处理事务,我们将时间单位设定为连续的七天。用户能够便捷地调整视角,浏览接下来七日的计划。系统在初始状态下,会依据当下的时间自动选定一个时间段并呈现。这相当于为用户呈现了一份明了的日程指引,帮助他们井然有序地规划每周的工作与生活。
触摸事件支持滑动切换月份
为了让用户能够便捷地更换月份,我们增加了滑动功能,通过触摸屏幕即可实现切换。用户向左或向右移动手指,就能浏览前一个月或后一个月的日历。这种交互方式十分流畅,如同翻阅一本充满魔力的历史典籍,既简单又方便,让用户在查看不同月份安排时更加轻松高效。
// 设置当前周期的日期
setSelectedPeriod(e) {
let temp = 1;
let today = 1;
if (this.data.currentPeriod[6] 1 <= 7) {
today = this.data.currentPeriod[6] 1;
}
if (this.data.todayDate == `${this.data.currentMonth}${this.data.today}日`) {
temp = 0;
today = this.data.today;
}
let startDate = today - (today % 7) 1;
let period = [];
for (let i = startDate; i < startDate 7; i ) {
if (i <= this.data.days.length) {
period.push(i);
}
}
// 自动补充下个月的日期
if (period.length < 7) {
let remainingDays = 7 - period.length;
let nextMonth = this.data.currentMonth 1;
if (nextMonth > 12) {
nextMonth = 1;
this.data.currentYear ;
}
for (let i = 1; i <= remainingDays; i ) {
period.push(i);
}
}
this.setData({
preselectedDate: this.data.selectedDate,
currentPeriod: period,
selectedDate: `${this.data.currentMonth}${period[0]}日`,
});
},
扩展功能与总结
先前完成的操作,使我们成功在微信小程序上构建了具备高级特性的日历模块。该日历不仅能够呈现日历日期,还与任务信息紧密关联,具备多样用途,有助于用户有效规划日程、增强工作效率。此外,可以根据具体要求增添更多特性,例如增加任务提示、修改和移除功能,以此提升用户的使用感受。
你们在制作相似应用时,有没有什么特别构思?速在留言区抒发,记得点击赞和转发本篇内容呀!
// 触摸开始事件
onTouchStart: function(e) {
this.setData({
touchStartX: e.changedTouches[0].pageX, // 记录触摸开始时的X轴坐标
});
},
// 触摸结束事件
onTouchEnd: function(e) {
this.setData({
touchEndX: e.changedTouches[0].pageX, // 记录触摸结束时的X轴坐标
});
if (this.data.touchEndX - this.data.touchStartX > 50) {
this.previousMonth(); // 切换到上个月
} else if (this.data.touchEndX - this.data.touchStartX < -50) {
this.nextMonth(); // 切换到下个月
}
},
Page({
data: {
todayDate: "2025年2月11日",
currentMonth: "2025年2月", // 当前月份
currentYear: "2025年", // 当前年份
days: [], // 月份的天数
selectedDate: null, // 选中的日期
preselectedDate: null, // 之前选中的日期
dayInfo: "", // 显示的附加信息
today: null, // 当前日期
year: 2025, // 当前年份
month: 2, // 当前月份
tasks: {
"task1": [],
"task2": [],
"task3": [],
"task4": []
},
newTaskContent: '', // 输入框内容
isModalVisible: false,
currentSection: "", // 当前操作的计划板块
isModalTimeVisible: false,
defaultDate: '',
startDate: '2025-02-12', // Default start date
startTime: '12:00', // Default start time
endDate: '2025-02-12', // Default start date
endTime: '12:00', // Default end time
showFullCalendar: false, // 是否展开显示所有日期
currentPeriod: [], // 当前显示的日期周期(7天)
touchStartX: 0, // 触摸开始时的X轴坐标
touchEndX: 0, // 触摸结束时的X轴坐标
adjustDays: [],
},
onLoad() {
const tasks = wx.getStorageSync('tasks') || {}; // 获取缓存中的任务数据,如果没有则使用空对象
this.getCurrentMonth();
this.generateCalendar();
// 获取当前日期
const today = new Date();
const day = today.getDate();
const month = today.getMonth() 1; // 月份是从0开始的,所以需要加1
const year = today.getFullYear();
const todayDate = year "年" month "月" day "日";
this.setData({
todayDate: todayDate, // 设置今天的日期
today: day, // 设置当前日期
month: month, // 设置当前月份
year: year, // 设置当前年份
currentYear: `${year}年`, // 设置当前显示的年份
currentMonth: `${year}年${month}月`, // 设置当前显示的年月
tasks,
});
this.updateCalendar();
// 处理 days 数组,去除 null,并截取前 7 天
let filteredDays = this.data.days.filter(day => day !== null).slice(0, 7);
this.setData({
filteredDays: filteredDays, // 将处理后的数据存储到 data 中
});
this.setSelectedPeriod(0); // 设置默认选中的周期
},
// 设置当前周期(7天一周期)
setSelectedPeriod: function(e) {
console.log(this.data.selectedDate)
let temp = 1;
let today = 1;
if (this.data.currentPeriod[6] 1 <= 7){
today = this.data.currentPeriod[6] 1;
}
if (this.data.todayDate == this.data.currentMonth this.data.today "日") {
temp = 0;
today = this.data.today;
} else if (e == 2 && (this.data.todayDate != this.data.currentMonth this.data.today "日")) {
temp = 2;
if (this.data.currentPeriod[0] <= 7) {
today = this.data.days.length this.data.currentPeriod[0] - 7;
} else {
today = this.data.currentPeriod[0] - 7;
}
}
let startDate = 0; // 获取当前日期所在周期的第一个日期
if (!temp) {
startDate = today - (today % 7) 1;
} else if (temp == 1) {
startDate = today;
} else {
startDate = today;
};
let period = [];
for (let i = startDate; i < startDate 7; i ) {
if (i <= this.data.days.length) {
period.push(i);
}
}
// 如果周期天数不足7天,自动补充下个月的日期,确保周期完整
if (period.length < 7) {
let remainingDays = 7 - period.length;
let nextMonth = this.data.currentMonth 1;
if (nextMonth > 12) {
nextMonth = 1;
this.data.currentYear ;
}
for (let i = 1; i <= remainingDays; i ) {
period.push(i);
}
}
this.setData({
preselectedDate: this.data.selectedDate,
currentPeriod: period,
selectedDate: this.data.currentMonth period[0] '日', // 默认选中周期的第一个日期
});
},
// 触摸开始事件
onTouchStart: function(e) {
this.setData({
touchStartX: e.changedTouches[0].pageX, // 记录触摸开始时的X轴坐标
});
},
// 触摸结束事件
onTouchEnd: function(e) {
this.setData({
touchEndX: e.changedTouches[0].pageX, // 记录触摸结束时的X轴坐标
});
this.handleSwipe();
},
// 处理滑动方向
handleSwipe: function() {
const startX = this.data.touchStartX;
const endX = this.data.touchEndX;
if (endX - startX > 50) {
// 向右滑动(上一周期)
this.changePeriod('right');
} else if (startX - endX > 50) {
// 向左滑动(下一周期)
this.changePeriod('left');
}
},
// 更改日期周期
changePeriod: function(direction) {
let preselectedDate = this.data.selectedDate;
let currentPeriodStart = this.data.currentPeriod[0]; // 获取当前周期的开始日期
let newPeriodStart = direction === 'left' ? currentPeriodStart 7 : currentPeriodStart - 7;
// 防止 newPeriodStart 小于最小有效日期范围
if (newPeriodStart < 1) {
let day = parseInt(preselectedDate.match(/(\d )日/)[0], 10);
newPeriodStart = day; // 如果超过最大日期,切换到上个月
}
// 防止 newPeriodStart 超过最大有效日期范围
if (this.data.days.length % 7 != 0 && newPeriodStart > this.data.days.length) {
let day = parseInt(preselectedDate.match(/(\d )日/)[0], 10);
newPeriodStart = day; // 如果超过最大日期,切换到下个月
} else if (this.data.days.length % 7 != 0 && newPeriodStart > this.data.days.length - (this.data.days.length % 7) 1) {
newPeriodStart = this.data.days.length - (this.data.days.length) 1; // 确保至少有7天
} else if (this.data.days.length % 7 == 0 && newPeriodStart > this.data.days.length - 7) {
newPeriodStart = this.data.days.length - 6; // 确保至少有7天
}
let newPeriod = [];
for (let i = newPeriodStart; i < newPeriodStart 7; i ) {
// 如果当前月份的日期还没有达到7天,开始补充下个月的日期
if (i <= this.data.days.length) {
newPeriod.push(i); // 当前月的日期
} else {
// 计算下个月的日期
let nextMonthDay = i - this.data.days.length;
let nextMonth = this.data.currentMonth 1; // 下一个月
if (nextMonth > 12) {
nextMonth = 1; // 如果是12月,跳转到1月
this.data.currentYear ; // 年份加1
}
newPeriod.push(nextMonthDay); // 下个月的日期
}
}
// 如果周期天数不足7天,自动补充下个月的日期,确保周期完整
if (newPeriod.length < 7) {
let remainingDays = 7 - newPeriod.length;
let nextMonth = this.data.currentMonth 1;
if (nextMonth > 12) {
nextMonth = 1;
this.data.currentYear ;
}
for (let i = 1; i <= remainingDays; i ) {
newPeriod.push(i);
}
}
this.setData({
currentPeriod: newPeriod,
selectedDate: this.data.currentMonth newPeriod[0] '日', // 更新为新周期的第一个日期
});
let selectedDate = this.data.selectedDate
// 判断是否需要切换到下个月
if (direction === 'left' && (preselectedDate == selectedDate)) {
this.switchToNextMonth(); // 只有在达到下个月时才会切换
return;
}
// 判断是否需要切换到上个月
if (direction === 'right' && (preselectedDate == selectedDate)) {
this.switchToPreviousMonth(); // 切换到上个月
return;
}
},
// 切换到下个月
switchToNextMonth: function() {
// 切换到下一个月的逻辑
const currentMonthArr = this.data.currentMonth.split('年');
let currentYear = parseInt(currentMonthArr[0]);
let currentMonth = parseInt(currentMonthArr[1].split('月')[0]);
currentMonth = 1; // 切换到下个月
if (currentMonth > 12) { // 如果当前月超过12月,切换到下一年
currentMonth = 1;
currentYear = 1;
};
const newMonth = currentYear '年' currentMonth '月';
const newYear = currentYear '年';
this.setData({
currentMonth: newMonth, // 更新当前月份
currentYear: newYear,
year: currentYear,
month: currentMonth,
days: this.updateDaysForNextMonth(currentMonth), // 获取下个月的日期
});
this.setSelectedPeriod(1); // 更新下个月的周期
},
// 切换到上个月
switchToPreviousMonth: function() {
const currentMonthArr = this.data.currentMonth.split('年');
let currentYear = parseInt(currentMonthArr[0]);
let currentMonth = parseInt(currentMonthArr[1].split('月')[0]);
currentMonth -= 1; // 切换到上个月
if (currentMonth < 1) { // 如果当前月小于1月,切换到上一年
currentMonth = 12;
currentYear -= 1;
}
const newMonth = currentYear '年' currentMonth '月';
const newYear = currentYear '年';
this.setData({
currentMonth: newMonth, // 更新当前月份
currentYear: newYear,
year: currentYear,
month: currentMonth,
days: this.updateDaysForPreviousMonth(currentMonth), // 获取上个月的日期
});
this.setSelectedPeriod(2); // 更新上个月的周期
this.updateCalendar();
},
// 获取下个月的日期
updateDaysForNextMonth: function(month) {
// 根据月份获取该月的天数,这里你需要提供逻辑来获取具体的天数
// 例如,你可以根据月份判断天数(28天,30天,31天等)
let daysInNextMonth = 30; // 这个是示例,你可以根据具体的月份调整
if (month === 2) {
daysInNextMonth = 28; // 2月处理
} else if ([4, 6, 9, 11].includes(month)) {
daysInNextMonth = 30; // 4月、6月、9月、11月有30天
} else {
daysInNextMonth = 31; // 其他月份有31天
}
let days = [];
for (let i = 1; i <= daysInNextMonth; i ) {
days.push(i);
}
this.onNextMonth();
return days;
},
// 获取上个月的日期
updateDaysForPreviousMonth: function(month) {
let daysInPrevMonth = 30; // 这个是示例,你可以根据具体的月份调整
if (month === 2) {
daysInPrevMonth = 28; // 2月处理
} else if ([4, 6, 9, 11].includes(month)) {
daysInPrevMonth = 30; // 4月、6月、9月、11月有30天
} else {
daysInPrevMonth = 31; // 其他月份有31天
}
let days = [];
for (let i = 1; i <= daysInPrevMonth; i ) {
days.push(i);
}
return days;
},
// 更新日历显示
updateCalendar() {
const { year, month } = this.data;
const firstDay = new Date(year, month - 1, 1); // 获取该月第一天
const lastDay = new Date(year, month, 0); // 获取该月最后一天
const daysInMonth = lastDay.getDate(); // 获取该月的天数
const days = [];
// 生成日期数组
for (let i = 1; i <= daysInMonth; i ) {
days.push(i);
}
this.setData({
days: days, // 更新日期数组
});
const firstDayOfMonth = new Date(this.data.year, this.data.month - 1, 1);
let firstDayWeek = firstDayOfMonth.getDay(); // 0 到 6,0是周日,1是周一,依此类推
// 调整周的起始日为周六 (0: Saturday, 1: Sunday, 2: Monday, ...)
firstDayWeek = (firstDayWeek 1) % 7; // 将周日(0)转换为 1,周一(1)转换为 2 ...,周六(6)转换为 0
// 获取上个月的最后一天
let prevMonthLastDay = new Date(this.data.year, this.data.month - 1, 0).getDate();
// 获取本月的总天数
const currentMonthLastDay = new Date(this.data.year, this.data.month, 0).getDate();
// 调整日期数组
const adjustedDays = [];
// 计算需要填充的上个月的天数
const prevMonthDays = [];
for (let i = prevMonthLastDay - firstDayWeek 1; i <= prevMonthLastDay; i ) {
prevMonthDays.push(i); // 填充上个月的日期
}
// 正序填充上个月的日期
adjustedDays.push(...prevMonthDays); // 将上个月的日期按顺序填充到前面
// 将本月的日期添加到 adjustedDays
adjustedDays.push(...this.data.days);
// 如果最后一行未满七天,填充下个月的日期
const lastWeekDays = adjustedDays.length % 7;
if (lastWeekDays !== 0) {
const nextMonthDays = [];
for (let i = 1; i <= 7 - lastWeekDays; i ) {
nextMonthDays.push(i); // 填充下个月的日期
}
adjustedDays.push(...nextMonthDays);
}
// 输出调整后的日期数组
console.log(adjustedDays);
this.setData({
adjustDays: adjustedDays,
});
},
// 切换日历展开/折叠
toggleCalendar() {
this.setData({
showFullCalendar: !this.data.showFullCalendar
});
},
// 切换到上一年
onPrevYear() {
let { year, month } = this.data;
year--;
this.setData({
year: year,
currentYear: `${year}年`, // 更新显示的年份
currentMonth: `${year}年${month}月`, // 更新显示的年月
});
this.updateCalendar();
},
// 切换到下一年
onNextYear() {
let { year, month } = this.data;
year ;
this.setData({
year: year,
currentYear: `${year}年`, // 更新显示的年份
currentMonth: `${year}年${month}月`, // 更新显示的年月
});
this.updateCalendar();
},
// 切换到上一个月
onPrevMonth() {
let { year, month } = this.data;
month--;
if (month < 1) {
month = 12;
year--;
}
this.setData({
year: year,
month: month,
currentMonth: `${year}年${month}月`,
});
this.updateCalendar();
},
// 切换到下一个月
onNextMonth() {
let { year, month } = this.data;
month ;
if (month > 12) {
month = 1;
year ;
}
this.setData({
year: year,
month: month,
currentMonth: `${year}年${month}月`,
});
this.updateCalendar();
},
// 获取当前月份并生成日历
getCurrentMonth() {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() 1;
this.setData({
currentMonth: `${year}年${month}月`
});
},
// 生成当前月份的日历
generateCalendar() {
const daysInMonth = new Date(new Date().getFullYear(), new Date().getMonth() 1, 0).getDate();
const days = [];
for (let i = 1; i <= daysInMonth; i ) {
days.push(i);
}
this.setData({
days: days
});
},
// 处理点击日期事件
onDayClick(e) {
const day = e.currentTarget.dataset.day;
this.setData({
selectedDate: `${this.data.currentMonth}${day}日`,
dayInfo: `你选择的日期是:${day}号。`
});
},
// 点击编辑计划时,弹出弹窗
onEditPlan(e) {
const section = e.currentTarget.dataset.section;
this.setData({
currentSection: section,
isModalVisible: true,
});
},
// 关闭弹窗
closeModal() {
this.setData({
isModalVisible: false
});
},
// 关闭弹窗
closeModalTime() {
this.setData({
isModalTimeVisible: false,
});
},
// 新增计划
addNewTask(e) {
// 使用 wx.showModal 来获取用户输入
wx.showModal({
title: '请输入计划内容',
editable: true,
placeholderText: '请填写计划内容',
success: (res) => {
if (res.confirm) {
this.setData({
isModalVisible: false, // 隐藏任务输入蒙版
isModalTimeVisible: true, // 显示时间选择蒙版
});
const content = res.content; // 获取用户输入
if (content) {
const { startDate, startTime, endDate, endTime } = this.data;
const newTask = { content, startDate, startTime, endDate, endTime };
const tasks = this.data.tasks;
const section = this.data.currentSection;
// 确保 tasks[section] 存在且是一个数组,如果不存在则初始化为空数组
if (!tasks[section]) {
tasks[section] = []; // 初始化为空数组
}
tasks[section].push(newTask); // 添加新任务
// 更新任务数据到本地缓存
wx.setStorageSync('tasks', tasks);
// 显示时间选择器
this.setData({
content: content, // 存储任务内容
tasks,
});
} else {
wx.showToast({
title: '请输入内容',
icon: 'none',
});
}
}
}
});
},
// 删除任务
deleteTask(e) {
const index = e.currentTarget.dataset.index;
const section = this.data.currentSection;
const tasks = this.data.tasks;
// 确保 tasks[section] 存在且是一个数组
if (tasks[section]) {
tasks[section].splice(index, 1); // 删除指定索引的任务
}
// 更新任务数据到本地缓存
wx.setStorageSync('tasks', tasks);
this.setData({
tasks
});
},
// Handle start date change
onStartDateChange(e) {
this.setData({
startDate: e.detail.value
});
},
// Handle start time change
onStartTimeChange(e) {
this.setData({
startTime: e.detail.value
});
},
// Handle end date change
onEndDateChange(e) {
this.setData({
endDate: e.detail.value
});
},
// Handle end time change
onEndTimeChange(e) {
this.setData({
endTime: e.detail.value
});
},
// 确认按钮,保存并关闭弹窗
confirmSelection: function (event) {
// 关闭弹窗
this.setData({
isModalTimeVisible: false,
isModalVisible: true,
});
},
onInputChange: function (event) {
// 更新输入框内容
this.setData({
newTaskContent: event.detail.value
});
},
});