uniapp----微信小程序 日历组件(周日历&& 月日历)【Vue3+ts+uView】
- 用Vue3+ts+uView来编写日历组件;
- 存在周日历和月日历两种显示方式;
- 高亮显示当天日期,红点渲染有数据的日期,点击显示数据
1. calendar-week-mouth组件代码
<template><view class="calender-container"><view class="calender-content"><!-- 头部 --><view class="calender-title" v-if="isWeek"><view class="calender-title-left">{{ checkedDay }}</view><view class="calender-title-morebtn" v-if="isMorebtn" @click="toggleMove">更多</view><view class="calender-title-right" @click="popupShowBtn" v-if="ispopupShow">?</view></view><view class="calender-title" v-if="!isWeek"><view class="calender-title-chevronl" @click="changeMonth(-1)"><text class="iconfont icon-back text-[28rpx]"></text></view><view class="calender-title-mouth">{{ MoutnTitle }}</view><view class="calender-title-chevronr calender-title-chevronr-right"><text class="iconfont icon-right text-[28rpx]" @click="changeMonth(1)"></text></view></view><!-- 星期头部 --><view class="calender-week-head"><view class="calender-week-head-item" v-for="(item, index) in WEEK_LIST" :key="index">{{ item.text }}</view></view><transition name="fade"><view class="calender-month-container" :class="{ transition: transition }" :style="{height: isWeek ? '120rpx' : '540rpx'}"><view v-for="(month, index) in monthList" :key="index" class="calender-month-item"><view v-for="(week, weekindex) in month" :key="weekindex" class="calender-month-week"><!-- :class="{ ischecked: checkedDay == day.date, istoday: day.istoday }" --><view v-for="(day, dayindex) in week" :class="{ ischecked: checkedDay == day.date }"@click.stop="chooseDay(day)" :key="dayindex" class="calender-month-week-item"><view class="calender-week-day" :class="{ischecked: checkedDay == day.date,othermonth: day.othermonth}"><span class="calender-one-day"><i class="day">{{day.othermonth === -1 || day.othermonth === 1? '': day.day}}</i></span><!-- 有事项标记 --><view class="thing" v-if="day.thing.task_time != null"><i class="dot"></i></view></view></view></view></view></view></transition></view><slot></slot></view><!-- 日历问号提示弹出框 --><w-calender-popup :popupShow="popupShow"></w-calender-popup>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, nextTick } from 'vue'const props = withDefaults(defineProps<{isWeek: booleanthings: Array<any> //日期对应的相关数据 数据格式 一维数组ispopupShow: booleanisMorebtn: boolean}>(),{isWeek: true, // true周 false 月ispopupShow: true, // 是否显示?问号弹窗 默认显示isMorebtn: false //是否显示日历更多按钮 默认不显示}
)const emits = defineEmits(['chooseDay', 'toggleMove']) //组件传递数据
const popupShow = ref<boolean>(false) //是否显示日历问号提示
// 打开提示框
const popupShowBtn = () => {popupShow.value = !popupShow.value
}// 头部星期列表
const WEEK_LIST = [{text: '日'},{text: '一'},{text: '二'},{text: '三'},{text: '四'},{text: '五'},{text: '六'}
]
const dateThing: any = ref([]) //某天事项// const things: any = ref([]) // 全部事项,用来插入到日历中
const dispDate = ref<Date>(new Date()) //当前时间type DayType = {date?: string | numberistoday?: booleanothermonth?: booleanthing?: []
}
type MonthList = DayType[]
const monthList: Record<string, any> = ref<MonthList>([])
const today = ref<Date>(new Date()) //当前时间
const MoutnTitle = ref('') //当前月份 x-x格式
const checkedDay = ref('') //选中时间
const currentDay = ref<Date>(new Date()) //当前时间
const transition = ref<boolean>(true) //是否显示动画const get3FullYear = ref(dispDate.value.getFullYear()) //定义当前年
const get3Monthz = ref(dispDate.value.getMonth()) //定义当前月
onMounted(() => {setTimeout(() => {todayDate()props.isWeek ? get3week() : get3month(get3FullYear.value, get3Monthz.value)initCalenderInfo()}, 200)
})
watch(() => props.things,async () => {await nextTick()todayDate()props.isWeek ? get3week() : get3month(get3FullYear.value, get3Monthz.value)initCalenderInfo()},{ immediate: true }
)
const selectDay = ref<Date>(new Date())
/*** 转换时间格式* @param date 标准时间*/
const formatDateTime = (date: Date): string => {const y = date.getFullYear()let m: string = date.getMonth() + 1 + ''m = Number(m) < 10 ? '0' + m : mlet d = date.getDate() + ''d = Number(d) < 10 ? '0' + d : dreturn y + '-' + m + '-' + d
}/*** 获取今天日期*/
const todayDate = () => {checkedDay.value = formatDateTime(today.value)selectDay.value = new Date(checkedDay.value)MoutnTitle.value = formatDateTime(today.value).substring(0, 7)
}
/*** 初始化当天事项*/
const initCalenderInfo = () => {const todayThing = monthList.value.flat(2).find((item: any) => item.date === checkedDay.value)?.thingdateThing.value = todayThing || []
}
/*** 返回该天事项* @param year 年* @param month 月* @param day 日*/
const ifOrder = (year: number, month: number, day: number) => {const dateTime = format(year, month, day)let dateItem = {}props.things.map((item: any) => {if (dateTime === item.task_time) {dateItem = item}})return dateItem
}/*** 转换时间* @param year 年* @param month 月* @param day 日*/
const format = (year: number, month: number, day: number | string) => {month++const m = month < 10 ? '0' + month : monthNumber(day) < 10 && (day = '0' + day)return year + '-' + m + '-' + day
}/*** 选中某一天* @param year 年* @param month 月* @param day 日* @param othermonth 其他月份,当前月前面空值* @param mode 类型,'month','week'* @param thing 事项*/
interface chooseDayParams {year: numbermonth: numberday: numberothermonth: numbermode: stringthing: Thing[]
}interface Thing {date: stringinfos?: ThingInfo[]
}interface ThingInfo {title: stringaddress: stringdates: string
}/*** @description: 选中日期* @param {*} year* @param {*} month* @param {*} day* @param {*} othermonth* @param {*} mode* @param {*} thing* @return {*}*/
const chooseDay = ({ year, month, day, othermonth, mode, thing }: chooseDayParams): void => {currentDay.value = new Date(year, month - 1, day) //标准时间checkedDay.value = format(year, month - 1, day) //"2020-11-11"if (othermonth && mode === 'month') {const tmpDt = new Date(dispDate.value.getFullYear(), dispDate.value.getMonth() - othermonth)const maxday = tmpDt.getDate()const days = maxday < day ? maxday : daydispDate.value = new Date(year, month - othermonth, days)changeIndex(othermonth || 0, true)} else {dispDate.value = currentDay.value}dateThing.value = thing || []emits('chooseDay', checkedDay.value)
}/*** 获取三周*/
const get3week = () => {const year = dispDate.value.getFullYear()const month = dispDate.value.getMonth()const day = dispDate.value.getDate()monthList.value = []monthList.value.push(getWeek(year, month, day - 7))monthList.value.push(getWeek(year, month, day))monthList.value.push(getWeek(year, month, day + 7))
}/*** 获取星期* @param year 为选中当天的年* @param month 为选中当天的月* @param day 为选中当天的日*/
const getWeek = (year: number, month: number, day: number) => {const dt = new Date(year, month, day)const weekArr = []const dtFirst = new Date(year, month, day - ((dt.getDay() + 7) % 7))const week = []//循环选中当天所在那一周的每一天for (let j = 0; j < 7; j++) {const newdt = new Date(dtFirst.getFullYear(), dtFirst.getMonth(), dtFirst.getDate() + j)const years = newdt.getFullYear()const months = newdt.getMonth()const days = newdt.getDate()const weekItem: weekParams = {mode: 'week',day: days,year: years,month: months + 1,date: format(years, months, days),//日历要显示的其他内容thing: ifOrder(years, months, days),istoday:today.value.getFullYear() === years &&today.value.getMonth() === months &&today.value.getDate() === days? true: false,ischecked: false,othermonth: months !== month}week.push(weekItem)}weekArr.push(week)return weekArr
}/*** 获取三个月(上月,本月,下月)*/
const get3month = (year: any, month: any) => {monthList.value = []monthList.value.push(getMonth(year, month - 1))monthList.value.push(getMonth(year, month))monthList.value.push(getMonth(year, month + 1))
}
const MonthType = ref(0) //0 当前月 -1上一个月 1下一个月
let Mnum = 1 //计数
let Ynum = 0// 点击上一个月 或者下一个月
const changeMonth = (type: number) => {MonthType.value = typeconst date = new Date()const year = date.getFullYear()const month = date.getMonth()let nextYear = year - Ynumlet chMonth = month + Mnumif (type === -1) {// 上一个月Mnum -= 1chMonth = month + MnumYnum = chMonth <= 0 ? Ynum - 1 : YnumchMonth = chMonth <= 0 ? 12 + chMonth : chMonth}if (type === 1) {// 下一个月Mnum += 1chMonth = month + MnumYnum = chMonth > 12 ? Ynum + 1 : YnumchMonth = chMonth > 12 ? chMonth - 12 : chMonth}nextYear = year + Ynumget3FullYear.value = nextYear //修改当前年get3Monthz.value = chMonth - 1 //修改当前月get3month(get3FullYear.value, get3Monthz.value)const newMonthTitle = `${nextYear}-${chMonth < 10 ? '0' + chMonth : chMonth}`MoutnTitle.value = newMonthTitle
}interface weekParams {mode: stringday: numberyear: numbermonth: numberdate: string//日历要显示的其他内容thing: ReturnType<typeof ifOrder>istoday: booleanischecked: booleanothermonth?: number | boolean
}/*** 创建单月历 顺序是从周日到周六* @param year 年* @param month 月*/
const getMonth = (year: number, month: number): DayType => {const monthArr = [] as anyconst dtFirst = new Date(year, month, 1) // 每个月第一天const dtLast = new Date(year, month + 1, 0) // 每个月最后一天const monthLength = dtLast.getDate() // 月份天数const firstDayOfWeek = dtFirst.getDay() // 第一天是星期几const rows = Math.ceil((monthLength + firstDayOfWeek) / 7) // 表格显示行数for (let i = 0; i < rows; i++) {const week = []for (let j = 0; j < 7; j++) {const day = i * 7 + j + 1 - firstDayOfWeekif (day > 0 && day <= monthLength) {const weekItem: weekParams = {mode: 'month',day: day,year: year,month: month + 1,date: format(year, month, day),// 日历要显示的其他内容thing: ifOrder(year, month, day),istoday:today.value.getFullYear() === year &&today.value.getMonth() === month &&today.value.getDate() === day? true: false,ischecked: false,othermonth: 0}week.push(weekItem)} else {// 其它月份const newDt = new Date(year, month, day)const years = newDt.getFullYear()const months = newDt.getMonth()const days = newDt.getDate()const weeksItem: weekParams = {mode: 'month',day: days,year: years,month: months,date: format(year, month, day),thing: ifOrder(year, month, day),istoday:today.value.getFullYear() === years &&today.value.getMonth() === months &&today.value.getDate() === days? true: false,ischecked: false,othermonth: day <= 0 ? -1 : 1}week.push(weeksItem)}}monthArr.push(week)}return monthArr
}
/*** 左右移动* @param index 月的index* @param isWeek 是否显示周* @param isClick 移动不可点击*/
const changeIndex = (index: number, isClick = false) => {if (props.isWeek) {dispDate.value = new Date(dispDate.value.getFullYear(),dispDate.value.getMonth(),dispDate.value.getDate() + 7 * index)currentDay.value = dispDate.valueget3week()} else {const tmpDt = new Date(dispDate.value.getFullYear(), dispDate.value.getMonth() + index, 0)const maxday = tmpDt.getDate()const days = maxday < dispDate.value.getDate() ? maxday : dispDate.value.getDate()dispDate.value = new Date(dispDate.value.getFullYear(),dispDate.value.getMonth() + index,days)if (!isClick) {checkedDay.value = format(dispDate.value.getFullYear(),dispDate.value.getMonth(),dispDate.value.getDate())}get3month(get3FullYear.value, get3Monthz.value)}initCalenderInfo()
}/*** 切换月或周* @param e event*/
const toggleMove = () => {emits('toggleMove')
}
</script>
<style scoped lang="scss">
.calender {&-container {width: 100%;}&-content {color: #666666;}&-title {display: flex;&-left {width: 70%;}&-right {position: absolute;right: 60rpx;width: 50rpx;height: 50rpx;border: 1px solid #e51c15;color: #e51c15;line-height: 44rpx;text-align: center;border-radius: 50%;font-size: 32rpx;padding-left: 14rpx;}&-morebtn {border: 2rpx solid #e51c15;// padding: 10rpx 40rpx;width: 120rpx;height: 46rpx;line-height: 46rpx;text-align: center;color: #e51c15;box-sizing: border-box;font-size: 24rpx;margin-right: 20rpx;border-radius: 10rpx;}&-chevronl text,&-chevronr text {color: #e51c15;font-size: 28rpx;font-weight: 400;&-right {text-align: right;}}&-mouth {width: 92%;text-align: center;font-size: 32rpx;color: #666666;}}&-week-head {width: 100%;display: flex;align-items: center;padding-top: 20px;font-size: 24rpx;font-weight: bold;&-item {// width: 14.2%;flex: 1;text-align: center;}}&-month {&-container {display: flex;position: relative;height: 460rpx;}&-item {position: absolute;width: 100%;min-height: 128rpx;padding: 30rpx 0;box-sizing: border-box;}&-item:nth-child(1) {left: -110%;}&-item:nth-child(2) {left: 0;}&-item:nth-child(3) {left: 110%;}&-week {display: flex;align-items: center;&-item {// width: 14.2%;flex: 1;text-align: center;position: relative;}}}&-week-day {display: block;text-align: center;font-style: normal;padding: 2rpx;line-height: 60rpx;height: 80rpx;width: 80rpx;}&-one-day {font-size: 24rpx;}
}.istoday .day,
.ischecked .day {width: 60rpx;height: 60rpx;color: #fff;background: #e51c15;border-radius: 50%;line-height: 60rpx;text-align: center;
}// .ischecked {
// border-radius: 50px;
// color: #fff !important;
// background: #7687e9;
// }
.thing {position: absolute;left: 34%;// bottom: 2px;transform: translateX(-50%);color: #e51c15;
}.thing .dot {display: block;width: 12rpx;height: 12rpx;word-break: break-all;line-height: 12rpx;color: #e51c15;background: #e51c15;border-radius: 50%;margin-top: 6rpx;
}
</style>
2. 在页面引用组件
<template>
<calendar-week-mouth :things="things" @chooseDay.stop="chooseDay" :isMorebtn="true" @toggleMove="toggleMove" ispopupShow :isWeek="isWeek">
</calendar-week-mouth>
</template><script setup lang="ts">
import { ref, watch, nextTick, shallowRef } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
onLoad(async (options) => {tag.value = Number(options.tag) || 1isWeek.value = tag.value === 1 || false
})
const tag = ref(1) //tag 1是周日历显示 2是月日历显示
const isWeek = ref(true)/*** @description: 点击单个日期获取选中的日期* @param {*} date:选中的日期 xxxx-xx-xx* @return {*}*/
const chooseDay = (date: string) => {checkedDay.value = date
}// 点击更多
const toggleMove = () => {uni.navigateTo({ url: '/package-legal/task-list/task-list?tag=2' })
}
/**
things数据结构
重要的是task_time字段
[{
id: 4,
status: 3,
task_time: "2023-07-26",
task_title: "",
time: "222",
}]
*/
</script>