如何在项目里面添加一个可以左右翻动并显示指定日期的日历

文章目录


前言

在我的健康系统中我有一个经期记录模块,我想写一个日历组件,可以左右翻动,也可以用不同的颜色来显示经期,排卵期,下一次经期以及当天日期,我用到的技术栈是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++;
};

总结

这个组件比较高级,思路也比较复杂。

相关推荐
前端 贾公子2 小时前
Vite 如何优化项目的图片体积
vue.js
柯南二号2 小时前
【大前端】【Android】用 Python 脚本模拟点击 Android APP —— 全面技术指南
android·前端·python
Arvin_Rong2 小时前
前端动态 API 生成与封装:前端 API 调用的一种思路
前端
2401_860319522 小时前
DevUI组件库实战:从入门到企业级应用的深度探索,如何实现支持表格扩展和表格编辑功能
前端·前端框架
码界奇点2 小时前
基于Django与Vue.js的RBAC权限管理系统设计与实现
vue.js·python·车载系统·django·毕业设计·源代码管理
LYFlied2 小时前
从循环依赖检查插件Circular Dependency Plugin源码详解Webpack生命周期以及插件开发
前端·webpack·node.js·编译原理·plugin插件开发
麒qiqi2 小时前
【Linux 系统编程】文件 IO 与 Makefile 核心实战:从系统调用到工程编译
java·前端·spring
IT_陈寒3 小时前
Vue3 性能优化实战:从10秒到1秒的5个关键技巧,让你的应用飞起来!
前端·人工智能·后端
gambool3 小时前
新版chrome Edge浏览器不再支持手动添加cookie
前端·chrome·edge