vue2实现一个带阴历,节气,节日的日历组件

1,500 阅读8分钟

本文介绍了如何使用 JavaScript 处理中国农历日历,并提供了一组实用的功能,用于将农历日期转换为数字、获取每月的天数、确定每月的第一天和最后一天的星期几等任务。这些功能有助于创建一个具有农历和阳历详细信息的日历应用。

先看效果图

image.png

引入依赖 首先,我们需要引入所需的依赖项,包括 dateFormaterlunar-javascript

dateFormater是对日期进行格式化的函数

lunar-javascript 使用npm install lunar-javascript

选择月份的用的是iviewUI库

import { dateFormater } from "@/common.js"
import { Solar, Lunar } from 'lunar-javascript'
  1. hineseDateToNumber个函数用于将中文农历日期转换为数字值,包括年、月和日。它接受一个中文日期字符串作为输入,并返回一个包含数字值的对象。
// 中文日期转数字
export const chineseDateToNumber = function (chineseDate) {
  const yearMap = {
    '0': 0, 'O': 0, '〇': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '十二': 12,
  }
  const monthMap = {
    '正': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '腊': 12, '冬': 11,
    '闰正': 1, '闰二': 2, '闰三': 3, '闰四': 4, '闰五': 5, '闰六': 6, '闰七': 7, '闰八': 8, '闰九': 9, '闰十': 10, '闰十一': 11, '闰冬': 11, '闰腊': 12,
  }
  const dayMap = {
    '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '十二': 12,
    '十三': 13, '十四': 14, '十五': 15, '十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20, '廿一': 21, '廿二': 22,
    '廿三': 23, '廿四': 24, '廿五': 25, '廿六': 26, '廿七': 27, '廿八': 28, '廿九': 29, '三十': 30, '三十一': 31,
  }
  let [year, res1] = chineseDate.split('年')
  let [month, day] = res1.split('月')
  year = year.split('').map(v => yearMap[v]).join('')
  month = monthMap[month.replace('闰', '')]
  day = day.replace('初', '')
  day = dayMap[day]
  return { year: Number(year), month, day }
}
  1. etDaysByMonth函数用于获取指定年份和月份的天数。它返回该月的总天数。
// 判断当前月有多少天
export const getDaysByMonth = function (year, month) {
  return new Date(year, month + 1, 0).getDate()
}
  1. etFirstDayByMonths个函数用于确定指定月份的第一天是星期几。返回值是一个数字,其中 0 表示星期日,1 表示星期一,依此类推。
// 当前月的第一天是星期几
export const getFirstDayByMonths = function (year, month) {
  return new Date(year, month, 1).getDay()
}
  1. etLastDayByMonth函数用于确定指定月份的最后一天是星期几。返回值与 getFirstDayByMonths 类似。
// 当前月的最后一天是星期几
export const getLastDayByMonth = function (year, month) {
  const lastDay = new Date(year, month + 1, 0).getDate();
  return new Date(year, month, lastDay).getDay();
}

5.etCurrentYearMonth函数用于设置当前的年份、月份和日期。它接受一个日期对象作为可选参数。

// 获取年月日
export const setCurrentYearMonth = function (d = this.shareDate) {
  let year = d.getFullYear()
  let month = d.getMonth()
  let date = d.getDate()
  this.showYearMonth = {
    year,
    month,
    date,
  }
}

6.reateCalendar函数用于创建一个包含农历和阳历详细信息的当前月份的日历。它填充了一个包含日期对象和相关信息的数组。

export const createCalendar = async function () {
  // 一天有多少毫秒
  const oneDayMS = 24 * 60 * 60 * 1000
  let list = []
  let { year, month } = this.showYearMonth
  // 当前月份第一天是星期几
  let firstDay = this.getFirstDayByMonths(year, month)
  // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下
  let prefixDaysLen = firstDay === 0 ? 6 : firstDay - 1
  // 向前移动之后的毫秒数
  let begin = new Date(year, month, 1).getTime() - oneDayMS * prefixDaysLen
  // 当前月份最后一天是星期几
  let lastDay = this.getLastDayByMonth(year, month)
  // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下 
  let suffixDaysLen = lastDay === 0 ? 0 : 7 - lastDay
  // 向后移动之后的毫秒数
  let end = new Date(year, month + 1, 0).getTime() + oneDayMS * suffixDaysLen
  // 填充天
  while (begin <= end) {
    let currentDate = new Date(begin)
    let curYear = currentDate.getFullYear()
    let curMonth = currentDate.getMonth()
    let date = currentDate.getDate()
    list.push({
      year: curYear,
      month: curMonth + 1, // 月是从0开始的
      date: date,
      value: dateFormater('YYYY-MM-DD', currentDate),
      disable: curMonth !== month,
    })
    begin += oneDayMS
  }
  list = list.map(v => {
    var solar = Solar.fromDate(new Date(v.value))
    // 获取阴历
    v.lunar = solar.getLunar().toString()
    v._lunar = v.lunar.substring(v.lunar.length - 2)
    // 获取阳历节日
    v.festivals = solar.getFestivals().toString()
    // 获取节气
    const timeObj = this.chineseDateToNumber(v.lunar)
    const res = Lunar.fromYmd(timeObj.year, timeObj.month, timeObj.day)
    v.jieQi = res.getJieQi() || ''
    // 获取阴历节日
    v._festivals = res.getFestivals().join(' ') || ''
    return v
  })
  this.calendarList = list
}

7.hangeDate数用于更改选定的日期并更新日历。它接受一个日期作为参数,并将其设置为选定日期,然后重新生成日历。

// 切换日期
export const changeDate = function (date) {
  this.shareDate = new Date(date)
  this.setCurrentYearMonth()
  this.createCalendar() // 创建当前月对应日历的日期数据
}

8.nitDataFun函数用于初始化日历的数据。它设置了一个默认的当前日期,然后调用 etCurrentYearMonthreateCalendar以生成初始的日历数据。

// 初始化数据
export const initDataFun = function () {
  this.shareDate = new Date()
  this.setCurrentYearMonth()
  this.createCalendar()
  this.getNow()
}

9.getNow 函数用于获取当前日期的农历、阳历节日和节气信息。它使用 lunar-javascript 库来获取相关信息。

// 获取当前日期的农历,节气,节日
export const getNow = function () {
  var solar = Solar.fromDate(new Date())
  let nowLunar = solar.getLunar().toString()
  // 农历日期
  this.lunar = nowLunar.substring(nowLunar.length - 4)
  // 阳历节日
  this.gregorianFestival = solar.getFestivals().toString()
  // 中文日期转数字
  const timeObj = this.chineseDateToNumber(nowLunar)
  const res = Lunar.fromYmd(timeObj.year, timeObj.month, timeObj.day)
  // 农历节日
  this.lunarFestival = res.getFestivals().join(' ') || ''
  // 节气
  this.jieQi = res.getJieQi() || ''
}

10.nowTime 函数用于返回当前月份的日历。它将选定日期设置为当前日期,然后调用 setCurrentYearMonthcreateCalendar 以生成当前月份的日历数据。

// 回到当前月份
export const nowTime = function(){
  this.shareDate=new Date()
  this.setCurrentYearMonth()
  this.createCalendar() // 创建当前月对应日历的日期数据
}

下面给出完整的代码,为了便于维护将一个vue文件拆分成4个模块,分别引入

HTML代码

<template>
  <div style="background-color: skyblue;" class="pl20 pr20">
    <h1 class="tc">带阴历-阳历-节日-节气的日历组件</h1>
    <div class="f xb ac mt10 mb10">
      <div class="b">
        当前时间:{{ currentTime }}
        农历{{ lunar }}
        <!-- 节气 -->
        {{ jieQi }}
        <!-- 阳历节日 -->
        {{ gregorianFestival }}
        <!-- 阴历节日 -->
        {{ lunarFestival }}
      </div>
      <div>
        <Button type="success" class="mr10" @click="nowTime">回到当前月份</Button>
        <DatePicker type="month" placeholder="选择年月" style="width: 200px" v-model="shareDate" @on-change="changeDate" :clearable="false" :editable="false"></DatePicker>
      </div>
    </div>
    <!-- 第一排的星期几 -->
    <div class="f b" style="border: 1px #000 solid;border-right: 0;">
      <div 
        class="f1 tc pt20 pb20" 
        style="background-color: pink;border-right: 1px #000 solid;"
        v-for="(item, index) in weekList" :key="index"
        >
        {{ item }}
      </div>
    </div>
    <div class="f" style="flex-wrap: wrap; border: 1px solid #000;border-left: 0;border-top: 0;border-bottom: 0;">
      <div 
        v-for="item, index in calendarList" :key="index"
        style="width: calc( 100% / 7);height: 150px;border-left: 1px solid #000;border-bottom: 1px solid #000;"
        :class="['pl10 pt10',item.value==$Z.dateFormater('YYYY-MM-DD',new Date())?'today':'',item.disable ? 'allowed' : 'chooseDay']"
        >
        <div :class="['f',item.disable?'g9':'gred']">
          <div class="mr10 b">{{ item.date }}</div>
          <!-- 如果有节日或节气就显示节日或节气,否则显示阴历 -->
          <div>{{ item.festivals ? item.festivals : item._festivals ? item._festivals : item.jieQi ? item.jieQi : item._lunar }}</div>
        </div>
      </div>
    </div>
  </div>
</template>
<script src='./index.js'></script>
<style src='./index.css' scoped></style>

index.css代码

.today{
    background-color: rgba(255, 165, 0,.4);
}
.chooseDay:hover{
    background-color: rgba(255, 165, 0,.8);
}
.allowed{
    cursor: not-allowed;
}

index.js代码

import * as func from "./func.js"
import { dateFormater } from "@/common.js"
export default {
  name: '',
  components: {},
  data() {
    return {
      showYearMonth: {}, // 显示的年月
      calendarList: [], // 用于遍历显示
      shareDate: '', // 
      weekList: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"], // 周
      now: new Date(),
      lunar:'',    //农历
      lunarFestival:'',   //农历节日
      jieQi:'',     //节气
      gregorianFestival:''   //阳历节日
    }
  },
  methods: {
    ...func,
  },
  created() {
    this.initDataFun()
  },
  mounted() {
    setInterval(() => {
      this.now = new Date()
    }, 1000);
  },
  computed: {
    currentTime() {
      return dateFormater('YYYY-MM-DD hh:mm:ss', this.now)
    },
  },
}

func.js代码

import { dateFormater } from "@/common.js"
import { Solar, Lunar } from 'lunar-javascript'
// 中文日期转数字
export const chineseDateToNumber = function (chineseDate) {
  const yearMap = {
    '0': 0, 'O': 0, '〇': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '十二': 12,
  }
  const monthMap = {
    '正': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '腊': 12, '冬': 11,
    '闰正': 1, '闰二': 2, '闰三': 3, '闰四': 4, '闰五': 5, '闰六': 6, '闰七': 7, '闰八': 8, '闰九': 9, '闰十': 10, '闰十一': 11, '闰冬': 11, '闰腊': 12,
  }
  const dayMap = {
    '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '十二': 12,
    '十三': 13, '十四': 14, '十五': 15, '十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20, '廿一': 21, '廿二': 22,
    '廿三': 23, '廿四': 24, '廿五': 25, '廿六': 26, '廿七': 27, '廿八': 28, '廿九': 29, '三十': 30, '三十一': 31,
  }
  let [year, res1] = chineseDate.split('年')
  let [month, day] = res1.split('月')
  year = year.split('').map(v => yearMap[v]).join('')
  month = monthMap[month.replace('闰', '')]
  day = day.replace('初', '')
  day = dayMap[day]
  return { year: Number(year), month, day }
}
// 判断当前月有多少天
export const getDaysByMonth = function (year, month) {
  return new Date(year, month + 1, 0).getDate()
}
// 当前月的第一天是星期几
export const getFirstDayByMonths = function (year, month) {
  return new Date(year, month, 1).getDay()
}
// 当前月的最后一天是星期几
export const getLastDayByMonth = function (year, month) {
  const lastDay = new Date(year, month + 1, 0).getDate();
  return new Date(year, month, lastDay).getDay();
}
// 获取年月日
export const setCurrentYearMonth = function (d = this.shareDate) {
  let year = d.getFullYear()
  let month = d.getMonth()
  let date = d.getDate()
  this.showYearMonth = {
    year,
    month,
    date,
  }
}
export const createCalendar = async function () {
  // 一天有多少毫秒
  const oneDayMS = 24 * 60 * 60 * 1000
  let list = []
  let { year, month } = this.showYearMonth
  // 当前月份第一天是星期几
  let firstDay = this.getFirstDayByMonths(year, month)
  // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下
  let prefixDaysLen = firstDay === 0 ? 6 : firstDay - 1
  // 向前移动之后的毫秒数
  let begin = new Date(year, month, 1).getTime() - oneDayMS * prefixDaysLen
  // 当前月份最后一天是星期几
  let lastDay = this.getLastDayByMonth(year, month)
  // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下 
  let suffixDaysLen = lastDay === 0 ? 0 : 7 - lastDay
  // 向后移动之后的毫秒数
  let end = new Date(year, month + 1, 0).getTime() + oneDayMS * suffixDaysLen
  // 填充天
  while (begin <= end) {
    let currentDate = new Date(begin)
    let curYear = currentDate.getFullYear()
    let curMonth = currentDate.getMonth()
    let date = currentDate.getDate()
    list.push({
      year: curYear,
      month: curMonth + 1, // 月是从0开始的
      date: date,
      value: dateFormater('YYYY-MM-DD', currentDate),
      disable: curMonth !== month,
    })
    begin += oneDayMS
  }
  list = list.map(v => {
    var solar = Solar.fromDate(new Date(v.value))
    // 获取阴历
    v.lunar = solar.getLunar().toString()
    v._lunar = v.lunar.substring(v.lunar.length - 2)
    // 获取阳历节日
    v.festivals = solar.getFestivals().toString()
    // 获取节气
    const timeObj = this.chineseDateToNumber(v.lunar)
    const res = Lunar.fromYmd(timeObj.year, timeObj.month, timeObj.day)
    v.jieQi = res.getJieQi() || ''
    // 获取阴历节日
    v._festivals = res.getFestivals().join(' ') || ''
    return v
  })
  this.calendarList = list
  console.log(list);
}
// 切换日期
export const changeDate = function (date) {
  this.shareDate = new Date(date)
  this.setCurrentYearMonth()
  this.createCalendar() // 创建当前月对应日历的日期数据
}
// 初始化数据
export const initDataFun = function () {
  this.shareDate = new Date()
  this.setCurrentYearMonth()
  this.createCalendar()
  this.getNow()
}
// 获取当前日期的农历,节气,节日
export const getNow = function () {
  var solar = Solar.fromDate(new Date())
  let nowLunar = solar.getLunar().toString()
  // 农历日期
  this.lunar = nowLunar.substring(nowLunar.length - 4)
  // 阳历节日
  this.gregorianFestival = solar.getFestivals().toString()
  // 中文日期转数字
  const timeObj = this.chineseDateToNumber(nowLunar)
  const res = Lunar.fromYmd(timeObj.year, timeObj.month, timeObj.day)
  // 农历节日
  this.lunarFestival = res.getFestivals().join(' ') || ''
  // 节气
  this.jieQi = res.getJieQi() || ''
}
// 回到当前月份
export const nowTime = function(){
  this.shareDate=new Date()
  this.setCurrentYearMonth()
  this.createCalendar() // 创建当前月对应日历的日期数据
}