使用场景描述:
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>