1.日历组件类
<template>
<div v-if="showPicker" class="window-backdrop">
<div class="window-wrapper">
<div class="today-date-text">
<div class="text-button" @click="cancle">取消</div>
<img class="arrow-img" @click="toLastMonth" src="../assets/arrow_left.png">
<div class="today-date">{{currentDateText}}</div>
<img class="arrow-img" @click="toNextMonth" src="../assets/arrow_right.png">
<div class="text-button" @click="confirm">确定</div>
</div>
<div class="week-wrapper">
<div class="week-item">周日</div>
<div class="week-item">周一</div>
<div class="week-item">周二</div>
<div class="week-item">周三</div>
<div class="week-item">周四</div>
<div class="week-item">周五</div>
<div class="week-item">周六</div>
</div>
<div class="month-day-wrapper">
<div @click="handleDay(day)" v-for="day in days"
:class="day.selected ? 'day-item-actived' : 'day-item'">
<div class="date-text">{{day.isToday ? '今天' : day.dateText}}</div>
<div class="chinese-date-text">{{day.chineseDateText}}</div>
</div>
<div class="day-item" v-for="item in (7 - days.length % 7)"></div>
</div>
</div>
</div>
</template>
<script>
import {
toLunar
} from './utils.js'
export default {
data() {
return {
days: [], // 当前月的所有日期的数据集合
currentDateText: '', // 头部标题年月
currentDate: '', // 初始化时为当前日期
showPicker: false
}
},
methods: {
open() {
this.showPicker = true
this.initPicker()
},
confirm() {
this.$emit('confirm', this.getSelectedDay())
},
cancle() {
this.showPicker = false
},
getSelectedDay() {
for (var i = 0; i < this.days.length; i++) {
if (this.days[i].selected) return this.days[i]
}
},
/**
* 日历点击事件,更新点击选中状态
* @param {Object} day
*/
handleDay(day) {
if (!day.date) return
for (var i = 0; i < this.days.length; i++) {
this.days[i].selected = day.dateText === this.days[i].dateText
}
this.updateTitle(day)
},
/**
* 更新标题年月
* @param {Object} day
*/
updateTitle(day) {
this.currentDateText = this.getDateText(day.date)
},
/**
* 下个月
*/
toLastMonth() {
this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() - 1, 1)
this.days = this.getPickerData(this.currentDate)
this.currentDateText = this.getDateText(this.currentDate)
},
/**
* 上个月
*/
toNextMonth() {
this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1, 1)
this.days = this.getPickerData(this.currentDate)
this.currentDateText = this.getDateText(this.currentDate)
},
/**
* 设置日历标题
* @param {Object} day
*/
getDateText(day) {
return day.getFullYear() + '年' + (day.getMonth() + 1) + '月'
},
/**
* 转换成周几
* @param {Object} day
*/
getWeekText(day) {
let weekVal = day.getDay()
let weekText = ''
switch (weekVal) {
case 0:
weekText = '日'
break
case 1:
weekText = '一'
break
case 2:
weekText = '二'
break
case 3:
weekText = '三'
break
case 4:
weekText = '四'
break
case 5:
weekText = '五'
break
case 6:
weekText = '六'
break
}
return '周' + weekText
},
/**
* 计算某个月的天数
* @param {Object} month
*/
getMonthDaysLength(day, month) {
day.setMonth(month)
day.setDate(0)
return day.getDate()
},
/**
* 根据日期对象计算当前月份1日为星期几,
* 从而计算日期数组开始需要添加几个空数据
* @param {Object} day
*/
getEmptyDays(day) {
return new Date(day.setDate(1)).getDay()
},
/**
* 根据传入的day对象获取当前月的所有日期数据
* @param {Object} day
*/
getPickerData(day) {
let arr = []
let emptyLength = this.getEmptyDays(day)
for (var i = 0; i < emptyLength; i++) {
arr.push({}) // 根据当前月份1号是星期几向往数组里添加空数据
}
let month = day.getMonth() + 1
let daysLength = this.getMonthDaysLength(day, month) // 计算当前月份一共有多少天
for (var i = 0; i < daysLength; i++) {
let obj = {
date: new Date(day.setDate(i + 1)), // Date类型的日期数据
dateText: i + 1, // 用于显示的日期
chineseDateText: toLunar(day.getFullYear(), month, i + 1).day, // 农历日期
isToday: new Date().toDateString() === new Date(day.setDate(i + 1)).toDateString(), // 是否是今天
selected: new Date().toDateString() === new Date(day.setDate(i + 1))
.toDateString() // 设置今天为选中状态
}
arr.push(obj)
}
return arr
},
initPicker() {
this.currentDate = new Date()
this.currentDateText = this.getDateText(this.currentDate) // 初始化标题年月
this.days = this.getPickerData(this.currentDate) // 默认当前日期初始化日历
}
}
}
</script>
<style scoped lang="scss">
* {
box-sizing: border-box;
}
.window-backdrop{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .6);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.window-wrapper{
width: 80%;
min-width: 300px;
max-width: 800px;
padding: 10px 15px;
background: #fff;
border-radius: 10px;
border: 1px #000 solid;
}
.week-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
font-weight: bold;
.week-item {
font-size: 14px;
flex: 14.2857%;
text-align: center;
}
}
.today-date-text {
display: flex;
height: 50px;
align-items: center;
justify-content: space-between;
line-height: 100%;
padding: 0 20px;
.text-button {
font-size: 16px;
color: #000;
}
.arrow-img {
height: 30px;
width: 30px;
}
.today-date {
font-size: 18px;
font-weight: bold;
}
}
.month-day-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.day-item,
.day-item-actived {
flex: 14.2857%;
padding: 10px 0;
margin-bottom: 5px;
font-size: 13px;
cursor: pointer;
overflow: hidden;
font-weight: bold;
.date-text, .chinese-date-text {
text-align: center;
}
}
.day-item {
color: #333;
}
.day-item-actived {
color: #fff;
background: #2875dd;
border-radius: 5px;
}
}
</style>
2.引用页面类
<template>
<div>
<div @click="showDatePicker" style="margin-bottom: 20px;text-align: center;">打开日历</div>
<date-picker ref="datePicker"></date-picker>
</div>
</template>
<script>
import datePicker from './date-picker.vue'
export default {
components: {
datePicker
},
methods:{
showDatePicker(){
this.$refs.datePicker.open()
}
}
}
</script>
<style scoped></style>
3.公历转农历工具类
var MIN_YEAR = 1891
var MAX_YEAR = 2100
var lunarInfo = [
[0, 2, 9, 21936],
[6, 1, 30, 9656],
[0, 2, 17, 9584],
[0, 2, 6, 21168],
[5, 1, 26, 43344],
[0, 2, 13, 59728],
[0, 2, 2, 27296],
[3, 1, 22, 44368],
[0, 2, 10, 43856],
[8, 1, 30, 19304],
[0, 2, 19, 19168],
[0, 2, 8, 42352],
[5, 1, 29, 21096],
[0, 2, 16, 53856],
[0, 2, 4, 55632],
[4, 1, 25, 27304],
[0, 2, 13, 22176],
[0, 2, 2, 39632],
[2, 1, 22, 19176],
[0, 2, 10, 19168],
[6, 1, 30, 42200],
[0, 2, 18, 42192],
[0, 2, 6, 53840],
[5, 1, 26, 54568],
[0, 2, 14, 46400],
[0, 2, 3, 54944],
[2, 1, 23, 38608],
[0, 2, 11, 38320],
[7, 2, 1, 18872],
[0, 2, 20, 18800],
[0, 2, 8, 42160],
[5, 1, 28, 45656],
[0, 2, 16, 27216],
[0, 2, 5, 27968],
[4, 1, 24, 44456],
[0, 2, 13, 11104],
[0, 2, 2, 38256],
[2, 1, 23, 18808],
[0, 2, 10, 18800],
[6, 1, 30, 25776],
[0, 2, 17, 54432],
[0, 2, 6, 59984],
[5, 1, 26, 27976],
[0, 2, 14, 23248],
[0, 2, 4, 11104],
[3, 1, 24, 37744],
[0, 2, 11, 37600],
[7, 1, 31, 51560],
[0, 2, 19, 51536],
[0, 2, 8, 54432],
[6, 1, 27, 55888],
[0, 2, 15, 46416],
[0, 2, 5, 22176],
[4, 1, 25, 43736],
[0, 2, 13, 9680],
[0, 2, 2, 37584],
[2, 1, 22, 51544],
[0, 2, 10, 43344],
[7, 1, 29, 46248],
[0, 2, 17, 27808],
[0, 2, 6, 46416],
[5, 1, 27, 21928],
[0, 2, 14, 19872],
[0, 2, 3, 42416],
[3, 1, 24, 21176],
[0, 2, 12, 21168],
[8, 1, 31, 43344],
[0, 2, 18, 59728],
[0, 2, 8, 27296],
[6, 1, 28, 44368],
[0, 2, 15, 43856],
[0, 2, 5, 19296],
[4, 1, 25, 42352],
[0, 2, 13, 42352],
[0, 2, 2, 21088],
[3, 1, 21, 59696],
[0, 2, 9, 55632],
[7, 1, 30, 23208],
[0, 2, 17, 22176],
[0, 2, 6, 38608],
[5, 1, 27, 19176],
[0, 2, 15, 19152],
[0, 2, 3, 42192],
[4, 1, 23, 53864],
[0, 2, 11, 53840],
[8, 1, 31, 54568],
[0, 2, 18, 46400],
[0, 2, 7, 46752],
[6, 1, 28, 38608],
[0, 2, 16, 38320],
[0, 2, 5, 18864],
[4, 1, 25, 42168],
[0, 2, 13, 42160],
[10, 2, 2, 45656],
[0, 2, 20, 27216],
[0, 2, 9, 27968],
[6, 1, 29, 44448],
[0, 2, 17, 43872],
[0, 2, 6, 38256],
[5, 1, 27, 18808],
[0, 2, 15, 18800],
[0, 2, 4, 25776],
[3, 1, 23, 27216],
[0, 2, 10, 59984],
[8, 1, 31, 27432],
[0, 2, 19, 23232],
[0, 2, 7, 43872],
[5, 1, 28, 37736],
[0, 2, 16, 37600],
[0, 2, 5, 51552],
[4, 1, 24, 54440],
[0, 2, 12, 54432],
[0, 2, 1, 55888],
[2, 1, 22, 23208],
[0, 2, 9, 22176],
[7, 1, 29, 43736],
[0, 2, 18, 9680],
[0, 2, 7, 37584],
[5, 1, 26, 51544],
[0, 2, 14, 43344],
[0, 2, 3, 46240],
[4, 1, 23, 46416],
[0, 2, 10, 44368],
[9, 1, 31, 21928],
[0, 2, 19, 19360],
[0, 2, 8, 42416],
[6, 1, 28, 21176],
[0, 2, 16, 21168],
[0, 2, 5, 43312],
[4, 1, 25, 29864],
[0, 2, 12, 27296],
[0, 2, 1, 44368],
[2, 1, 22, 19880],
[0, 2, 10, 19296],
[6, 1, 29, 42352],
[0, 2, 17, 42208],
[0, 2, 6, 53856],
[5, 1, 26, 59696],
[0, 2, 13, 54576],
[0, 2, 3, 23200],
[3, 1, 23, 27472],
[0, 2, 11, 38608],
[11, 1, 31, 19176],
[0, 2, 19, 19152],
[0, 2, 8, 42192],
[6, 1, 28, 53848],
[0, 2, 15, 53840],
[0, 2, 4, 54560],
[5, 1, 24, 55968],
[0, 2, 12, 46496],
[0, 2, 1, 22224],
[2, 1, 22, 19160],
[0, 2, 10, 18864],
[7, 1, 30, 42168],
[0, 2, 17, 42160],
[0, 2, 6, 43600],
[5, 1, 26, 46376],
[0, 2, 14, 27936],
[0, 2, 2, 44448],
[3, 1, 23, 21936],
[0, 2, 11, 37744],
[8, 2, 1, 18808],
[0, 2, 19, 18800],
[0, 2, 8, 25776],
[6, 1, 28, 27216],
[0, 2, 15, 59984],
[0, 2, 4, 27424],
[4, 1, 24, 43872],
[0, 2, 12, 43744],
[0, 2, 2, 37600],
[3, 1, 21, 51568],
[0, 2, 9, 51552],
[7, 1, 29, 54440],
[0, 2, 17, 54432],
[0, 2, 5, 55888],
[5, 1, 26, 23208],
[0, 2, 14, 22176],
[0, 2, 3, 42704],
[4, 1, 23, 21224],
[0, 2, 11, 21200],
[8, 1, 31, 43352],
[0, 2, 19, 43344],
[0, 2, 7, 46240],
[6, 1, 27, 46416],
[0, 2, 15, 44368],
[0, 2, 5, 21920],
[4, 1, 24, 42448],
[0, 2, 12, 42416],
[0, 2, 2, 21168],
[3, 1, 22, 43320],
[0, 2, 9, 26928],
[7, 1, 29, 29336],
[0, 2, 17, 27296],
[0, 2, 6, 44368],
[5, 1, 26, 19880],
[0, 2, 14, 19296],
[0, 2, 3, 42352],
[4, 1, 24, 21104],
[0, 2, 10, 53856],
[8, 1, 30, 59696],
[0, 2, 18, 54560],
[0, 2, 7, 55968],
[6, 1, 27, 27472],
[0, 2, 15, 22224],
[0, 2, 5, 19168],
[4, 1, 25, 42216],
[0, 2, 12, 42192],
[0, 2, 1, 53584],
[2, 1, 21, 55592],
[0, 2, 9, 54560]
]
//是否闰年
function isLeapYear(year) {
return ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0))
}
//天干地支年
function lunarYear(year) {
var gan = ['庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己']
var zhi = ['申', '酉', '戌', '亥', '子', '丑', '寅', '卯', '辰', '巳', '午', '未']
var str = year.toString().split("")
return gan[str[3]] + zhi[year % 12]
}
//生肖年
function zodiacYear(year) {
var zodiac = ['猴', '鸡', '狗', '猪', '鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊']
return zodiac[year % 12]
}
//公历月份天数
//@param year 阳历-年
//@param month 阳历-月
function solarMonthDays(year, month) {
var FebDays = isLeapYear(year) ? 29 : 28
var monthHash = ['', 31, FebDays, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
return monthHash[month]
}
//农历月份天数
function lunarMonthDays(year, month) {
var monthData = lunarMonths(year)
return monthData[month - 1]
}
//农历月份天数数组
function lunarMonths(year) {
var yearData = lunarInfo[year - MIN_YEAR]
var leapMonth = yearData[0]
var bit = (+yearData[3]).toString(2)
var months = []
for (var i = 0; i < bit.length; i++) {
months[i] = bit.substr(i, 1)
}
for (var k = 0, len = 16 - months.length; k < len; k++) {
months.unshift('0')
}
months = months.slice(0, (leapMonth === 0 ? 12 : 13));
for (var i = 0; i < months.length; i++) {
months[i] = +months[i] + 29
}
return months
}
//农历每年的天数
//@param year 农历年份
function lunarYearDays(year) {
var monthArray = lunarYearMonths(year)
var len = monthArray.length
return (monthArray[len - 1] === 0 ? monthArray[len - 2] : monthArray[len - 1])
}
//
function lunarYearMonths(year) {
var monthData = lunarMonths(year)
var res = []
var temp = 0
var yearData = lunarInfo[year - MIN_YEAR]
var len = (yearData[0] === 0 ? 12 : 13)
for (var i = 0; i < len; i++) {
temp = 0
for (var j = 0; j <= i; j++) {
temp += monthData[j]
}
res.push(temp)
}
return res
}
//获取闰月
//@param year 农历年份
function leapMonth(year) {
var yearData = lunarInfo[year - MIN_YEAR]
return yearData[0]
}
//计算农历日期与正月初一相隔的天数
function betweenLunarDays(year, month, day) {
var yearMonth = lunarMonths(year)
var res = 0
for (var i = 1; i < month; i++) {
res += yearMonth[i - 1]
}
res += day - 1
return res
}
//计算2个阳历日期之间的天数
//@param year 阳历年
//@param month
//@param day
//@param l_month 阴历正月对应的阳历月份
//@param l_day 阴历初一对应的阳历天
function betweenSolarDays(year, month, day, l_month, l_day) {
var time1 = new Date(year + "-" + month + "-" + day).getTime()
var time2 = new Date(year + "-" + l_month + "-" + l_day).getTime()
return Math.ceil((time1 - time2) / 24 / 3600 / 1000)
}
//根据距离正月初一的天数计算阴历日期
//@param year 阳历年
//@param between 天数
function lunarByBetween(year, between) {
var lunarArray = {}
var yearMonth = []
var t = 0
var e = 0
var leapMonthVal = 0
var m = ''
if (between === 0) {
t = 1
e = 1
m = '正月'
} else {
year = between > 0 ? year : (year - 1)
yearMonth = lunarYearMonths(year)
leapMonthVal = leapMonth(year)
between = between > 0 ? between : (lunarYearDays(year) + between)
for (var i = 0; i < 13; i++) {
if (between === yearMonth[i]) {
t = i + 2
e = 1
break
} else if (between < yearMonth[i]) {
t = i + 1
e = between - ((yearMonth[i - 1]) ? yearMonth[i - 1] : 0) + 1
break
}
}
m = (leapMonthVal !== 0 && t === leapMonthVal + 1) ? ('闰'.chineseMonth(t - 1)) : chineseMonth(((leapMonthVal !== 0 && leapMonthVal + 1 < t) ? (t - 1) : t))
}
lunarArray.lunarDate = year + '-' + t + '-' + e // 年月日
lunarArray.repetMonth = leapMonthVal //闰几月
lunarArray.yearType = lunarYear(year) // 天干地支年
lunarArray.animal = zodiacYear(year) // 生肖年
lunarArray.month = m // 月份
lunarArray.day = chineseNumber(e, m) // 日
return lunarArray
}
//中文月份
function chineseMonth(month) {
var monthHash = ['', '正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月']
return monthHash[month]
}
//中文日期
function chineseNumber(num, month) {
var dateHash = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
var res = ''
if(num === 1){
res = month
}else if (num <= 10) {
res = '初' + dateHash[num]
} else if (num > 10 && num < 20) {
res = '十' + dateHash[num - 10]
} else if (num === 20) {
res = "二十"
} else if (num > 20 && num < 30) {
res = "廿" + dateHash[num - 20]
} else if (num === 30) {
res = "三十"
}
return res
}
//转换农历
export function toLunar(year, month, day) {
var yearData = lunarInfo[year - MIN_YEAR]
if (year === MIN_YEAR && month <= 2 && day <= 9) {
return [1891, 1, 1, '辛卯', '兔', '正月', '初一']
}
return lunarByBetween(year, betweenSolarDays(year, month, day, yearData[1], yearData[2]))
}
//转换公历
//@param year 阴历-年
//@param month 阴历-月,闰月处理:例如如果当年闰五月,那么第二个五月就传六月,相当于阴历有13个月
//@param date 阴历-日
export function toSolar(year, month, day) {
var yearData = lunarInfo[year - MIN_YEAR]
var between = betweenLunarDays(year, month, day)
var ms = new Date(year + "-" + yearData[1] + "-" + yearData[2]).getTime()
var s = ms + between * 24 * 60 * 60 * 1000
var d = new Date()
d.setTime(s)
year = d.getFullYear()
month = d.getMonth() + 1
day = d.getDate()
return [year, month, day]
}