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

文章目录


前言

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

总结

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

相关推荐
IT_陈寒6 小时前
Redis缓存击穿把我整不会了,原来还有这手操作
前端·人工智能·后端
idcu6 小时前
深入 Lyt.js 组件系统:L2 渲染引擎层的核心
前端·typescript
这是程序猿6 小时前
Spring Boot自动配置详解
java·大数据·前端
文心快码BaiduComate6 小时前
干货|Comate Harness Engineering工程实践指南
前端·后端·程序员
还有多久拿退休金6 小时前
一张栈的图,治好你面试答不出 script 阻塞的病
前端·javascript
光辉GuangHui6 小时前
Agent Skill 也需要测试:如何搭建 Skill 评估框架
前端·后端·llm
To_OC6 小时前
我终于搞懂 Claude Code 核心逻辑!90%的人都用错了模式
前端·ai编程
蓝宝石的傻话6 小时前
Headless浏览器的隐形陷阱:为什么你的AI自动化工具抓不到页面早期错误?
前端
irving同学462387 小时前
Node 后端实战:JWT 认证与生产级错误处理
前端·后端
莽夫搞战术7 小时前
【Google Stitch】AI原生画布重新定义设计,让想法变成可交互界面
前端·人工智能·ui