文章目录
前言
在我的健康系统中我有一个经期记录模块,我想写一个日历组件,可以左右翻动,也可以用不同的颜色来显示经期,排卵期,下一次经期以及当天日期,我用到的技术栈是Vue,Node.js,mongoBD数据库,下面我将讲讲我的思路。
一、调取数据
首先我们要调取我们储存在mongodb数据库里面的月经日期数据,因为在前面的表单里面我们只存储了月经来的日期以及他的周期所以我们要在前端用计算属性计算排卵期以及下一次月经预测时间。因为我们是要调取所有月经数据,所以是以数组的形式调取的。
Vue
onMounted(async () => {
try {
const res = await getMen();
const list = res?.data?.data || [];
if (!Array.isArray(list) || list.length === 0) {
console.log("无经期记录");
return;
}
periodList.value = list;
// 最新一条
const latest = list[0];
lastPeriod.value = toYMD(latest.startDate);
duration.value = latest.leaveTime;
cycle.value = latest.stayTime;
console.log('lastPeriod', lastPeriod.value);
} catch (e) {
console.error("获取失败", e);
}
});
而在这里把最新的一条数据和之前的数据分开获取主要是为了计算下一次月经的预期日期。
二、计算排卵期和预计下次月经日期
我们按照周期规律,利用计算属性来计算排卵期和预计下次月经日期,因为我们直接从数据库获得的日期数据不符合我们的标准,所以我们需要一个函数来转换那些日期数据具体代码如下
css
// periodDates: 返回 YYYY-MM-DD 字符串数组,表示经期每一天
const periodDates = computed(() => {
const result: string[] = [];
periodList.value.forEach(p => {
const start = ymdToDate(toYMD(p.startDate));
if (!start) return;
for (let i = 0; i < p.leaveTime; i++) {
const d = addDaysDate(start, i);
result.push(toYMD(d));
}
});
return result;
});
//显示下次月经的预测时间
const nextPeriodTime = computed(() => {
const r : string[] = []
const nextPeriodDate = addDaysDate(ymdToDate(lastPeriod.value) || new Date('2025-12-09'),cycle.value)
for(let i = 0;i<duration.value;i++){
const d = addDaysDate(nextPeriodDate,i)
r.push(toYMD(d))
}
console.log('下一次',r);
return r
})
const isToday = computed(() => {
const todayDate = toYMD(new Date())
return periodDates.value.includes(todayDate)
})
const activeTab = computed(() => !isToday.value);
//显示经期第几天
const periodIndex = computed(()=> {
const todayDate = toYMD(new Date())
const index = periodDates.value.indexOf(todayDate)
return index >=0 ? index + 1 : 0
})
//转换日期格式
function toYMD(input: string | Date | null | undefined): string {
if (!input && input !== '') return "";
const s = String(input);
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;
const d = new Date(input as any);
if (isNaN(d.getTime())) return "";
return d.toISOString().split("T")[0] || '';
}
// 给一个 Date 对象加天,返回新的 Date(本地时间)
function addDaysDate(d: Date, n: number): Date {
const r = new Date(d);
r.setDate(r.getDate() + n);
return r;
}
function ymdToDate(ymd: string): Date | null {
if (!ymd) return null;
const m = ymd.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!m) return null;
const year = Number(m[1]), month = Number(m[2]) - 1, day = Number(m[3]);
return new Date(year, month, day);
}
//计算排卵日
const ovulationDay = computed(() => {
if (!lastPeriod.value) return "";
const base = ymdToDate(lastPeriod.value);
if (!base) return "";
// 常用算法:排卵日 = 月经开始日 + (周期 - 14) 天
const ovDayIdx = Number(cycle.value) - 14;
const ovDate = addDaysDate(base, ovDayIdx);
return toYMD(ovDate);
});
// ovulationDates: 排卵日前后各5天(合计 11 天)
const ovulationDates = computed(() => {
const r: string[] = [];
periodList.value.forEach(p => {
const start = ymdToDate(toYMD(p.startDate));
if (!start) return;
const ovDayIdx = p.stayTime - 14;
const ovDate = addDaysDate(start, ovDayIdx);
for (let i = -5; i <= 5; i++) {
const d = addDaysDate(ovDate, i);
r.push(toYMD(d));
}
});
return r;
});
这样我们就得到了三个数组分别表示排卵期,月经期,下一次月经预计时期
三、渲染日历
我们通过遍历渲染日历,并且通过判断该日期是否在上述三个数组之间来给元素添加不同的了来达到预期效果
代码如下(示例):
c
const today = new Date();
const year = ref(today.getFullYear());
const month = ref(today.getMonth());
const weekLabels = ["日", "一", "二", "三", "四", "五", "六"];
const days = computed(() => {
const first = new Date(year.value, month.value, 1);
const last = new Date(year.value, month.value + 1, 0);
const result = [];
const start = first.getDay();
const total = last.getDate();
for (let i = 0; i < start; i++) {
result.push({
date: new Date(NaN),
isToday: false,
isPeriod: false,
isOvulation: false,
nextPeriod:false,
});
}
for (let i = 1; i <= total; i++) {
const d = new Date(year.value, month.value, i);
const key = toYMD(d);
result.push({
date: d,
isToday: key === toYMD(today),
isPeriod: periodDates.value.includes(key),
isOvulation: ovulationDates.value.includes(key),
nextPeriod: nextPeriodTime.value.includes(key)
});
}
return result;
});
const prevMonth = () => {
if (month.value === 0) {
month.value = 11;
year.value--;
} else month.value--;
};
const nextMonth = () => {
if (month.value === 11) {
month.value = 0;
year.value++;
} else month.value++;
};
总结
这个组件比较高级,思路也比较复杂。