375 lines
11 KiB
Vue
375 lines
11 KiB
Vue
<template>
|
||
<view class="calendar">
|
||
<slot name="head" v-bind:currentDate="currentDate">
|
||
<view class="header">
|
||
<view class="btn btn-prev fu-iconfont2" @click="onSwitchPrevMonth()"></view>
|
||
<view class="current-date">{{ currentDate }}</view>
|
||
<view class="btn btn-next fu-iconfont2" @click="onSwitchNextMonth()"></view>
|
||
</view>
|
||
</slot>
|
||
<view class="body">
|
||
<view class="weeks">
|
||
<view class="week-item" v-for="week in weeks" :key="week">{{ week }}</view>
|
||
</view>
|
||
<view class="day-list">
|
||
<view class="day-item" v-for="(day, index) in dayList" :key="index" @click="handleClick(day)">
|
||
<text v-if="day.checked" class="checked">{{ day.day }}</text>
|
||
<text v-else-if="day.isToday" class="today">{{ day.day }}</text>
|
||
<text v-else-if="day.type === 'current'" class="current-month-day">{{ day.day }}</text>
|
||
<text v-else>{{ day.day }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<script>
|
||
export default {
|
||
name: 'benben-sign-card',
|
||
props: {
|
||
signData: {
|
||
type: Array,
|
||
default() {
|
||
return []
|
||
},
|
||
},
|
||
keyName: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
currentMonth: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
setTimeoutFn: null,
|
||
year: null,
|
||
month: null,
|
||
day: null,
|
||
dayList: [],
|
||
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
||
}
|
||
},
|
||
computed: {
|
||
checks() {
|
||
if (this.keyName) {
|
||
return this.signData.map((item) => item[this.keyName])
|
||
} else {
|
||
return this.signData
|
||
}
|
||
},
|
||
currentDate() {
|
||
const year = this.year
|
||
const month = this.month < 10 ? '0' + this.month : this.month
|
||
return `${year}-${month}`
|
||
},
|
||
},
|
||
watch: {
|
||
checks() {
|
||
this.initDate()
|
||
},
|
||
},
|
||
created() {
|
||
const { year, month, day } = this.getCurrentDate()
|
||
this.initDate(year, month, day)
|
||
},
|
||
methods: {
|
||
// 初始化日期数据
|
||
initDate(year = this.year, month = this.month, day = this.day) {
|
||
// 获取日期列表
|
||
const dayList = this.getDayList(year, month)
|
||
// 更新年月日以及日期列表
|
||
this.year = year
|
||
this.month = month
|
||
this.day = day
|
||
this.dayList = dayList
|
||
},
|
||
// 处理点击事件
|
||
handleClick(day) {
|
||
// 构造数据
|
||
const payload = {
|
||
year: this.year,
|
||
month: this.month,
|
||
...day,
|
||
}
|
||
// 根据日期类型,执行相应操作
|
||
switch (day.type) {
|
||
case 'prev':
|
||
this.onSwitchPrevMonth(day)
|
||
break
|
||
case 'next':
|
||
this.onSwitchNextMonth(day)
|
||
break
|
||
default:
|
||
this.$emit('current-month-day', payload)
|
||
}
|
||
},
|
||
// 切换到上一个月
|
||
onSwitchPrevMonth(day) {
|
||
// 获取上一个月的年月信息
|
||
const { prevYear, prevMonth } = this.getPrevMonthInfo(this.year, this.month)
|
||
// 构造数据
|
||
const payload = {
|
||
year: prevYear,
|
||
month: prevMonth,
|
||
oldYear: this.year,
|
||
oldMonth: this.month,
|
||
...day,
|
||
}
|
||
// 初始化日期数据
|
||
this.initDate(prevYear, prevMonth)
|
||
// 触发事件
|
||
this.$emit('update:current-month', this.currentDate)
|
||
this.emitChange(payload)
|
||
},
|
||
// 切换到下一个月
|
||
onSwitchNextMonth(day) {
|
||
// 获取下一个月的年月信息
|
||
const { nextYear, nextMonth } = this.getNextMonthInfo(this.year, this.month)
|
||
// 构造数据
|
||
const payload = {
|
||
year: nextYear,
|
||
month: nextMonth,
|
||
oldYear: this.year,
|
||
oldMonth: this.month,
|
||
...day,
|
||
}
|
||
// 初始化日期数据
|
||
this.initDate(nextYear, nextMonth)
|
||
// 修改current-month的值
|
||
this.$emit('update:current-month', this.currentDate)
|
||
// 触发事件
|
||
this.emitChange(payload)
|
||
},
|
||
// 触发change
|
||
emitChange(payload) {
|
||
this.setTimeoutFn && clearTimeout(this.setTimeoutFn)
|
||
this.setTimeoutFn = setTimeout(() => {
|
||
this.$emit('change', payload)
|
||
}, 300)
|
||
},
|
||
// 获取上一个月的年月信息
|
||
getPrevMonthInfo(year, month) {
|
||
let prevYear = year
|
||
let prevMonth = month - 1
|
||
if (prevMonth < 1) {
|
||
prevMonth = 12
|
||
prevYear -= 1
|
||
}
|
||
return {
|
||
prevYear,
|
||
prevMonth,
|
||
}
|
||
},
|
||
// 获取下一个月的年月信息
|
||
getNextMonthInfo(year, month) {
|
||
let nextYear = year
|
||
let nextMonth = month + 1
|
||
if (nextMonth > 12) {
|
||
nextMonth = 1
|
||
nextYear += 1
|
||
}
|
||
return {
|
||
nextYear,
|
||
nextMonth,
|
||
}
|
||
},
|
||
// 获取日期列表
|
||
getDayList(year, month) {
|
||
// 获取前一个月的日期列表
|
||
const prevMonthDays = this.getPrevMonthDays(year, month)
|
||
// 获取当前月的日期列表
|
||
const currentMonthDays = this.getCurrentMonthDays(year, month)
|
||
// 获取下一个月的日期列表
|
||
const nextMonthDays = this.getNextMonthDays(year, month)
|
||
// 合并日期列表
|
||
return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays]
|
||
},
|
||
// 获取当前日期
|
||
getCurrentDate() {
|
||
// const date = this.currentMonth ? new Date(this.currentMonth) : new Date()
|
||
const date = new Date()
|
||
const year = date.getFullYear() // 获取当前年份
|
||
const month = date.getMonth() + 1 // 获取当前月份,注意需要加1,因为月份从0开始
|
||
const day = date.getDate() // 获取当前日期
|
||
return {
|
||
year,
|
||
month,
|
||
day,
|
||
}
|
||
},
|
||
// 获取下一个月天数
|
||
getMonthLastDay(year, month) {
|
||
// 根据传入的年份和月份获取下一个月的信息
|
||
const { nextYear, nextMonth } = this.getNextMonthInfo(year, month)
|
||
// 构建一个日期对象,设置为下一个月的第一天
|
||
const date = new Date(`${nextYear}/${nextMonth}/1`)
|
||
// 获取下一个月的第一天的时间戳
|
||
const firstDayTimeStamp = date.getTime()
|
||
const oneDayTimeStamp = 24 * 60 * 60 * 1000 // 一天的毫秒数
|
||
// 上一个月最后一天的日期
|
||
const prevMonthLastDay = new Date(firstDayTimeStamp - oneDayTimeStamp).getDate()
|
||
return prevMonthLastDay
|
||
},
|
||
// 获取上一个月的日期列表
|
||
getPrevMonthDays(year, month) {
|
||
// 根据传入的年份和月份获取上一个月的信息
|
||
const { prevYear, prevMonth } = this.getPrevMonthInfo(year, month)
|
||
// 获取上一个月的最后一天
|
||
const prevMonthLastDay = this.getMonthLastDay(prevYear, prevMonth)
|
||
// 构建一个日期对象,设置为当前月的第一天
|
||
const date = new Date(`${year}/${month}/1`)
|
||
// 获取当前月的第一天是星期几
|
||
const week = date.getDay()
|
||
const days = []
|
||
// 遍历上一个月的日期,并添加到日期列表中
|
||
for (let i = 0; i < week; i++) {
|
||
days.unshift({
|
||
type: 'prev',
|
||
checked: false,
|
||
day: prevMonthLastDay - i, // 依次从上一个月最后一天向前推
|
||
})
|
||
}
|
||
return days
|
||
},
|
||
// 获取当前月天数日期列表
|
||
// 参数:year-年份,month-月份
|
||
getCurrentMonthDays(year, month) {
|
||
// 获取当前月的最后一天
|
||
const currentMonthLastDay = this.getMonthLastDay(year, month)
|
||
// 获取已选日期
|
||
const checks = this.checks
|
||
// 存储当月所有日期的数组
|
||
const days = []
|
||
// 获取当前日期
|
||
const { year: currentYear, month: currentMonth, day: currentDay } = this.getCurrentDate()
|
||
// 遍历当月所有日期
|
||
for (let i = 1; i <= currentMonthLastDay; i++) {
|
||
let checked = false
|
||
let isToday = currentYear === year && currentMonth === month && currentDay === i
|
||
// 判断当前日期是否被选中
|
||
checks.forEach((date) => {
|
||
const dateArr = date.split('-')
|
||
if (year == dateArr[0] && month == dateArr[1] && i == dateArr[2]) {
|
||
checked = true
|
||
}
|
||
})
|
||
// 将日期对象添加到数组中
|
||
days.push({
|
||
type: 'current',
|
||
checked,
|
||
isToday,
|
||
day: i,
|
||
})
|
||
}
|
||
return days
|
||
},
|
||
// 获取下一个月天数
|
||
// 参数:year-年份,month-月份
|
||
getNextMonthDays(year, month) {
|
||
// 获取当前月的最后一天
|
||
const currentMonthLastDay = this.getMonthLastDay(year, month)
|
||
// 获取下一个月的最后一天对应的日期
|
||
const date = new Date(`${year}/${month}/${currentMonthLastDay}`)
|
||
// 获取下一个月的第一天是星期几
|
||
const week = date.getDay()
|
||
// 存储下一个月从第一天开始的日期
|
||
const days = []
|
||
let day = 0
|
||
// 遍历下一个月的日期
|
||
for (let i = week + 1; i <= 6; i++) {
|
||
day++
|
||
// 将日期对象添加到数组中
|
||
days.push({
|
||
type: 'next',
|
||
checked: false,
|
||
day,
|
||
})
|
||
}
|
||
return days
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
.calendar {
|
||
color: $uni-text-color;
|
||
.header {
|
||
padding: 0 24rpx;
|
||
justify-content: center;
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 2rpx solid #eee;
|
||
.current-date {
|
||
text-align: center;
|
||
font-size: 34rpx;
|
||
padding: 32rpx 0;
|
||
color: #030303;
|
||
margin: 0 24rpx;
|
||
}
|
||
.btn {
|
||
line-height: 1;
|
||
background: #fff;
|
||
border: 1px solid #dcdfe6;
|
||
color: #606266;
|
||
text-align: center;
|
||
box-sizing: border-box;
|
||
font-weight: 500;
|
||
padding: 12rpx 20rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
.body {
|
||
.weeks {
|
||
display: flex;
|
||
font-size: 30rpx;
|
||
padding: 32rpx 0;
|
||
.week-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
}
|
||
.day-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
.day-item {
|
||
display: flex;
|
||
justify-content: center;
|
||
text-align: center;
|
||
width: 14.285%;
|
||
padding: 15rpx 0;
|
||
font-size: 28rpx;
|
||
color: #c8c8c8;
|
||
line-height: 56rpx;
|
||
.current-month-day {
|
||
color: #171717;
|
||
}
|
||
.today {
|
||
width: 56rpx;
|
||
border-radius: 50%;
|
||
background: var(--benbenbgColor2);
|
||
color: #fff;
|
||
}
|
||
.checked {
|
||
color: #171717;
|
||
border-radius: 50%;
|
||
position: relative;
|
||
&::before {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
content: '';
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
border-radius: 50%;
|
||
background: var(--benbenbgColor7);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|