使用场景描述:
1.显示单个月的日期数据,点击切换月份数据
2.点击日期收起日历(显示一个星期),再点开展开日历(显示整个月)
ps:本例子用的是typescript写的
html模板
<div class="calendar"> <div class="calendar-top"> <div class="calendar-top-title">学习表</div> <div class="calendar-top-button"> <img src="../../assets/img/outside/icxxbgsy18.png" alt="" class="button-icon" @click="arrowLeft()" /> <div class="calendar-top-time" @click="clickDate"> {{ year }}-{{ month + 1 }} </div> <img src="../../assets/img/outside/icxxbgsy17.png" alt="" class="button-icon" @click="arrowRight()" /> </div> </div> <van-calendar class="calendar-main" :class="showAllMonth ? 'open' : 'close'" :show-title="false" :poppable="false" :show-confirm="false" :show-mark="false" :default-date="defaultDate" :min-date="minDate" :max-date="maxDate" :formatter="formatter" @select="selectDay" first-day-of-week="1" row-height="35px" ref="calendar" /> </div>
复制
模板解析
主要用到vant的calendar组件,组件上方显示所选月份加按钮控制切换月份,分别是arrowLeft()函数和arrowRight()函数。
显示单个月的日期数据,点击切换月份数据
1.创建一个函数,用于设置当前显示的月份日历,主要是修改min-date和max-date。在初始化数据时(例如onMounted)调用,并将当前月份作为参数传入。
//设置当前显示日历 const setDate = (month: number) => { //计算传入的月份天数 const daycount = new Date(year.value, month + 1, 0).getDate() //将1号设置成可选择最小日期 let monthMin = new Date(year.value, month, 1) //将传入月份的最后一天设置成可选择最大日期 let monthMax = new Date(year.value, month, daycount) //已在setup中初始化minDate和maxDate(minDate=ref()) minDate.value = monthMin maxDate.value = monthMax }
复制
2.切换上个月的日历
const arrowLeft = function () { //将当前所在月份减一,并传入setDate()函数 month.value -- setDate(month.value) }
复制
3.切换下个月的日历
const arrowRight= function () { //将当前所在月份加一,并传入setDate()函数 month.value ++ setDate(month.value) }
复制
可在arrowLeft ()和arrowRight()中做些判断限制可切换的月份,例如month.value不能一直减减到负数,或者不能一直加加到超过12,偷个懒这里就不写啦~~
点击日期收起日历,再点开展开日历
//获取实例(vue3写法) import { getCurrentInstance } from "vue" const { proxy } = getCurrentInstance() as any //点击日期响应函数 const clickDate = function () { //showAllMonth:是否展开日历 showAllMonth.value = !showAllMonth.value let calendarMain = <HTMLElement>( document.getElementsByClassName("calendar-main")[0] ) if (showAllMonth.value) { //展开,用样式控制 calendarMain.style.height = "225px" } else { //收起,用样式控制 calendarMain.style.height = "70px" //获得当前选择的日期(本例子是将选中日期的时间戳存在store中) //转时间戳是本例子项目需要,也可以不转时间戳直接date形式就行 let date = new Date(baseStore.selectTimestamp * 1000) //将选中日期重置到当前选中的日期,不然收起后选中的是1号,并且是显示1号所在的那个星期 proxy.$refs.calendar.reset(date) } }
复制
//选中任意日期响应函数 const selectDay = function (day: any) { // console.log(day)//选中的日期 const timestamp = Date.parse(day) / 1000 //转时间戳 //存store baseStore.$patch((state) => { state.selectTimestamp = timestamp }) }
复制
两个功能分享就到此为止啦,其他都是通过样式来控制的,修改vant组件中的样式。需要用到::v-deep来选择元素类才能选中到,例如::v-deep(.van-calendar)。vant组件的样式类名可通过浏览器中按f12选中元素来查看类名。
参考一下:
::v-deep(.van-calendar) { width: 106%; margin-left: -3%; background: none; } //在数字前面加上周字 ::v-deep(.van-calendar__weekday::before) { content: "周"; color: rgba($color: #000000, $alpha: 0.5); }
复制
---------------------------------------------这是一条分割线---------------------------------------------
满足下评论区伙计的要求,贴下源码,自己平时写不出到处搜的时候也很无助,希望能帮到各位码友(隔了太久我已经忘记当时的思路了QAQ)
<template> <div class="calendar"> <div class="calendar-top"> <div class="calendar-top-title">学习表</div> <div class="calendar-top-button"> <img src="../../assets/img/outside/icxxbgsy18.png" alt="" class="button-icon" @click="arrowLeft()" /> <div class="calendar-top-time" @click="clickDate"> {{ year }}-{{ month + 1 }} </div> <img src="../../assets/img/outside/icxxbgsy17.png" alt="" class="button-icon" @click="arrowRight()" /> </div> </div> <van-calendar class="calendar-main" :show-title="false" :poppable="false" :show-confirm="false" :show-mark="false" :default-date="defaultDate" :min-date="minDate" :max-date="maxDate" :formatter="formatter" @select="selectDay" first-day-of-week="1" row-height="35px" ref="calendar" /> </div> </template> <script lang="ts" setup> import { ref, reactive, watch, onMounted, computed, onActivated, getCurrentInstance, } from "vue" import { GetCalendar } from "../../api/appBase" import { useUserStore } from "../../store/model/user" import { useBaseStore } from "../../store/model/appBase" const userStore = useUserStore() const baseStore = useBaseStore() const { proxy } = getCurrentInstance() as any let dayDatas = reactive({ days: [] }) let defaultDate = ref(new Date()) let year = ref(defaultDate.value.getFullYear()) let month = ref(defaultDate.value.getMonth()) let currentMonth = ref(defaultDate.value.getMonth()) let showAllMonth = ref(false) let minDate = ref() let maxDate = ref() const formatter = (day: any) => { // console.log(dayDatas) //给有数据的日期加点 let isHasData = false let dayTimestamp = Date.parse(day.date) dayDatas.days.forEach((item) => { if (dayTimestamp == item["date"] * 1000) isHasData = true }) if (isHasData) { day.className = "addDot" } //日期被选中并且有数据 let selectDayTimestamp = baseStore.selectTimestamp * 1000 if (dayTimestamp == selectDayTimestamp && isHasData) { day.className = "addDot_Select" } //当天日期加样式 let todayTimestamp = new Date(new Date().toDateString()).getTime() if (dayTimestamp == todayTimestamp) { day.className = "calendarToday" } //当天日期有数据 if (dayTimestamp == todayTimestamp && isHasData) { day.className = "addDot_calendarToday" } return day } //初始化 从接口获得数据 const GetCalendarData = function (year: any, month: any) { const grade = userStore.getGrade const mode = 0 GetCalendar({ year, month, stage: grade, mode }).then((res) => { // console.log(res.data) dayDatas.days = res.data }) } //设置当前显示日历 const setDate = (month: number) => { const daycount = new Date(year.value, month + 1, 0).getDate() let monthMin = new Date(year.value, month, 1) let monthMax = new Date(year.value, month, daycount) minDate.value = monthMin maxDate.value = monthMax } //切换上个月 const arrowLeft = function () { // console.log("<") if (month.value != currentMonth.value - 1) { month.value = currentMonth.value - 1 setDate(month.value) GetCalendarData(year.value, month.value + 1) let day = new Date( year.value, month.value, new Date(year.value, month.value + 1, 0).getDate() ) selectDay(day) } else { proxy.$toast({ message: "仅保留本月和上个月的记录哦", icon: new URL(`../../assets/img/outside/icxxbgsy28.png`, import.meta.url) .href, className: "toast-c", }) } } //切换下个月 const arrowRight = function () { // console.log(">") if (month.value < currentMonth.value) { month.value++ setDate(month.value) GetCalendarData(year.value, month.value + 1) let day = new Date(year.value, month.value, 1) selectDay(day) } else { proxy.$toast({ message: "仅保留本月和上个月的记录哦", icon: new URL(`../../assets/img/outside/icxxbgsy28.png`, import.meta.url) .href, className: "toast-c", }) } } const selectDay = function (day: any) { // console.log(day) const timestamp = Date.parse(day) / 1000 baseStore.$patch((state) => { state.selectTimestamp = timestamp }) } const clickDate = function () { showAllMonth.value = !showAllMonth.value let calendarMain = <HTMLElement>( document.getElementsByClassName("calendar-main")[0] ) if (showAllMonth.value) { calendarMain.style.height = "225px" } else { calendarMain.style.height = "68px" let date = new Date(baseStore.selectTimestamp * 1000) proxy.$refs.calendar.reset(date) } } //特属于keepAlive的一个生命周期,activated在页面每次进入都会执行 onActivated(() => { if (baseStore.selectTimestamp != 0) { defaultDate.value = new Date(baseStore.selectTimestamp * 1000) } year.value = defaultDate.value.getFullYear() month.value = defaultDate.value.getMonth() GetCalendarData(year.value, month.value + 1) setDate(month.value) let calendarMain = <HTMLElement>( document.getElementsByClassName("calendar-main")[0] ) calendarMain.style.height = "70px" }) watch( () => userStore.getGrade, (newVal, oldVal) => { GetCalendarData(year.value, month.value + 1) } ) </script> <style lang="scss" scoped> .calendar { // border: 1px solid red; margin-bottom: 46px !important; .calendar-top { margin-bottom: 16px; display: flex; justify-content: space-between; .calendar-top-title { font-size: 32px; font-weight: bold; } .calendar-top-button { display: flex; font-size: 28px; align-items: center; .button-icon { width: 32px; height: 32px; } .calendar-top-time { color: rgba($color: #007aea, $alpha: 0.5); margin: 0 10px; } } } .calendar-weekdays { display: flex; height: 50px; justify-content: space-between; align-items: center; // border: 1px solid red; .calendar-weekdays-item { width: 50px; font-size: 20px; color: rgba($color: #000000, $alpha: 0.5); // border: 1px solid red } } .calendar-days { display: grid; grid-template-columns: repeat(7, 8%); grid-row-gap: 14px; height: 50px; justify-content: space-between; align-items: center; .calendar-days-item { flex-shrink: 0; width: 50px; font-size: 28px; color: #000000; // border: 1px solid red; } } } ::v-deep(.van-calendar) { width: 106%; margin-left: -3%; background: none; } ::v-deep(.van-calendar__body) { overflow: hidden; } ::v-deep(.van-calendar__header) { box-shadow: none; } // ::v-deep(.van-calendar__day) { // margin-bottom: 6px !important; // } ::v-deep(.van-calendar__selected-day) { border-radius: 50%; background-color: #59afff !important; z-index: 2; position: absolute; } ::v-deep(.van-calendar__selected-day::after) { background-color: #fff !important; } ::v-deep(.van-calendar__header-subtitle) { display: none; } ::v-deep(.van-calendar__weekday) { color: rgba($color: #000000, $alpha: 0.5); } ::v-deep(.van-calendar__weekday::before) { content: "周"; color: rgba($color: #000000, $alpha: 0.5); } //有数据日期加点 ::v-deep(.addDot) { position: relative; } ::v-deep(.addDot::after) { position: absolute; content: ""; width: 12px; height: 12px; top: 26Px; left: 45px; border-radius: 50%; background-color: #0084ff; } //当天日期 ::v-deep(.calendarToday) { position: relative; color: #fff; // font-size: 0; // z-index: 1; } ::v-deep(.calendarToday::before) { width: 35Px; height: 35Px; line-height: 35Px; position: absolute; top: 0; content: "今"; text-align: center; font-size: 30px; border-radius: 50%; background-color: #ffae34; // z-index: -1; } //当天日期并且有数据 ::v-deep(.addDot_calendarToday::after) { position: absolute; content: ""; width: 12px; height: 12px; top: 26Px; left: 45px; border-radius: 50%; background-color: #fff; z-index: 4; } ::v-deep(.addDot_calendarToday::before) { width: 35Px; height: 35Px; line-height: 35Px; position: absolute; top: 0; content: "今"; text-align: center; font-size: 30px; border-radius: 50%; color: #fff; background-color: #ffae34; } //有数据并且被选中 ::v-deep(.addDot_Select::after) { position: absolute; content: ""; width: 12px; height: 12px; top: 26Px; left: 45px; border-radius: 50%; background-color: #fff; z-index: 3; } //被选中显示的今字 ::v-deep(.calendarToday .van-calendar__selected-day) { font-size: 0; } ::v-deep(.calendarToday .van-calendar__selected-day::before) { content: "今"; z-index: 9; font-size: 30px; height: 35Px; line-height: 35Px; } ::v-deep(.addDot_calendarToday .van-calendar__selected-day) { font-size: 0; } ::v-deep(.addDot_calendarToday .van-calendar__selected-day::before) { content: "今"; z-index: 9; font-size: 30px; height: 35Px; line-height: 35Px; } </style> <style lang="scss"> .toast-c { width: 450px; height: 60px; padding: 22px 40px; min-height: 60px; display: flex; flex-direction: row; justify-content: center; align-items: center; .van-toast__icon { font-size: 48px; margin-right: 10px; } .van-toast__text { font-size: 28px; margin-top: 0; } } </style>
复制