使用场景描述:

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>
Logo

前往低代码交流专区

更多推荐