vue3自定义日历

原理

  • 现在的日历分为两种开头:
    1. '日', '一', '二', '三', '四', '五', '六'
    2. '一', '二', '三', '四', '五', '六', '日'
  • 一行7个日期,一共6行

  1. 其实不管哪种都一样,首先要确定第一行1号在哪个位置。
    如果说是 '日', '一', '二', '三', '四', '五', '六',那么getDay()是几,它前面就该补几位。当前是2023-7-1,这天是星期六,getDay() === 6前面补六天,如果是星期天getDay()===0前面不用补。这里就要去取上个月多少天了,减去这六天,就可以把前面的空补上。例如:上个月是6月,有30天,30-6+1=25,从25补到30就可以了。这里是需要判断闰年这些的,防止在2月取的不对。
  2. 第一行补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
  3. 现在算最后面还需要补多少天,6行一共42天,现在渲染了6+31=37天,还需要补42-37=5天。
2526272829301

在这里插入图片描述

  1. 其实不管哪种都一样,首先要确定第一行1号在哪个位置。
    如果说是 '一', '二', '三', '四', '五', '六','日',这里就有点小区别了。假设n=getDay(),应该补n-1天, 但是星期日例外,星期日的n=0但是它要补6天。当前是2023-7-1,这天是星期六,getDay() === 6前面补五天,如果是星期天getDay()===0前面补六天。
  2. 第一行补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
  3. 现在算最后面还需要补多少天,6行一共42天,现在渲染了5+31=36天,还需要补42-36=6天。
262728293012

在这里插入图片描述

源码

<template>
  <div>
    <div class="toolbar">
      <div class="btn" @click="preMonth">上一月</div>
      <div class="date">{{ currentYear }}年{{ currentMonth }}月</div>
      <div class="btn" @click="nextMonth">下一月</div>
    </div>
    <div class="container">
      <div v-for="(item, index) in header" :key="index" class="grid-item">{{ item }}</div>
      <div v-for="(item, index) in lastMonthSurplusDayArray" :key="index" class="grid-item">
        {{ item }}
      </div>
      <div
        v-for="(item, index) in currentMonthDayCount"
        :key="index"
        :class="[
          'grid-item',
          Math.random() * 30 < 10
            ? 'very-hot'
            : Math.random() * 30 < 20
            ? 'middle-hot'
            : 'normal-hot'
        ]"
      >
        {{ item }}
      </div>
      <div v-for="(item, index) in nextMonthSurplusDayArray" :key="index" class="grid-item">
        {{ item }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
// const header = reactive<string[]>(['日', '一', '二', '三', '四', '五', '六'])
const header = reactive<string[]>(['一', '二', '三', '四', '五', '六', '日'])
// 上个月剩余天数
const lastMonthSurplusDay = ref<number>(0)
const lastMonthSurplusDayArray = ref<number[]>([])
// 下个月剩余天数
const nextMonthSurplusDay = ref<number>(0)
const nextMonthSurplusDayArray = ref<number[]>([])
// 当前月份总天数
const currentMonthDayCount = ref<number>(0)
const currentYear = ref<number>(0)
const currentMonth = ref<number>(0)
const currentDate = ref<number>(0)
// 闰年
const leapMonthDay = reactive<number[]>([31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31])
// 平年
const normalMonthDay = reactive<number[]>([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31])
// 是否为闰年
function isLeapYear(year: number) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}
// 获取这个月1号的星期数
function getMonthFirstDay(year: number, month: number) {
  return new Date(year, month - 1, 1).getDay()
}
// 计算日期
function calculateDays() {
  // 获取本月第一天星期几(星期几就补多少个空)
  // lastMonthSurplusDay.value = getMonthFirstDay(currentYear.value, currentMonth.value)
  lastMonthSurplusDay.value =
    getMonthFirstDay(currentYear.value, currentMonth.value) === 0
      ? 6
      : getMonthFirstDay(currentYear.value, currentMonth.value) - 1
  // 获取当前月有多少天
  currentMonthDayCount.value = isLeapYear(currentYear.value)
    ? leapMonthDay[currentMonth.value - 1]
    : normalMonthDay[currentMonth.value - 1]
  let prevMonthLastDate = 0
  if (currentMonth.value === 1) {
    // 当前是1月还要用去年的去判断
    prevMonthLastDate = isLeapYear(currentYear.value - 1)
      ? leapMonthDay[leapMonthDay.length - 1]
      : normalMonthDay[normalMonthDay.length - 1]
  } else {
    prevMonthLastDate = isLeapYear(currentYear.value)
      ? leapMonthDay[currentMonth.value - 2]
      : normalMonthDay[currentMonth.value - 2]
  }
  // 获取还需要渲染多少天
  nextMonthSurplusDay.value = 42 - (lastMonthSurplusDay.value + currentMonthDayCount.value)
  const prevtemp = []
  const nexttemp = []
  for (let i = prevMonthLastDate - lastMonthSurplusDay.value + 1; i <= prevMonthLastDate; i++) {
    prevtemp.push(i)
  }
  for (let i = 1; i <= nextMonthSurplusDay.value; i++) {
    nexttemp.push(i)
  }
  lastMonthSurplusDayArray.value = prevtemp
  nextMonthSurplusDayArray.value = nexttemp
}
// 上个月
function preMonth() {
  if (currentMonth.value === 1) {
    currentMonth.value = 12
    --currentYear.value
  } else {
    --currentMonth.value
  }
  calculateDays()
}
// 下个月
function nextMonth() {
  if (currentMonth.value === 12) {
    currentMonth.value = 1
    ++currentYear.value
  } else {
    ++currentMonth.value
  }
  calculateDays()
}
// 获取当前日期
function getCurrentDate() {
  const d = new Date()
  const year = d.getFullYear()
  const month = d.getMonth() + 1
  const date = d.getDate()
  return {
    year,
    month,
    date
  }
}
// 初始化日历
function initCalendar() {
  const { year, month, date } = getCurrentDate()
  currentYear.value = year
  currentMonth.value = month
  currentDate.value = date
  calculateDays()
}
onMounted(() => {
  initCalendar()
})
</script>

<style scoped>
.toolbar {
  display: flex;
  width: 100%;
  height: 50px;
}

.toolbar .date {
  flex: 1;
  color: #333;
  font-size: 14px;
  font-weight: bold;
  text-align: center;
  line-height: 30px;
  border-top: 1px solid #eee;
  padding: 10px;
  box-sizing: border-box;
}
.toolbar .btn {
  flex: 1;
  color: #1d84f6;
  text-align: center;
  line-height: 30px;
  border-top: 1px solid #eee;
  border-left: 1px solid #eee;
  border-right: 1px solid #eee;
  padding: 10px;
  box-sizing: border-box;
}
.container {
  display: grid;
  box-sizing: border-box;
  grid-template-columns: auto auto auto auto auto auto auto;
  border-top: 1px solid #eee;
  border-left: 1px solid #eee;
  border-right: 1px solid #eee;
}
.grid-item {
  width: 50px;
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
  border-right: 1px solid #eee;
  font-size: 14px;
  text-align: center;
}

.grid-item:nth-child(7n) {
  border-right: none;
}

.very-hot {
  background-color: #e10231;
}
.middle-hot {
  background-color: #fe4806;
}
.normal-hot {
  background-color: #ffb508;
}
</style>
### Vue3 实现日历组件 示例教程 在 Vue3 中实现日历功能可以通过组合式 API 和自定义组件的方式完成。以下是基于 Vue3日历组件实现的一个简单示例。 #### 安装依赖 为了简化开发流程,可以使用 `dayjs` 或者其他日期处理库来辅助操作日期数据[^1]。通过 npm 安装所需的依赖项: ```bash npm install dayjs ``` #### 创建日历组件 (Calendar.vue) 下面是一个基本的日历组件结构,它展示了当前月份的所有天数并允许用户点击选择特定日期。 ```vue <template> <div class="calendar"> <h2>{{ currentMonth }} {{ currentYear }}</h2> <div class="weekdays"> <span v-for="weekday in weekdays" :key="weekday">{{ weekday }}</span> </div> <div class="dates-grid"> <span v-for="(date, index) in dates" :key="index" :class="{ selected: isSelected(date), today: isToday(date) }" @click="selectDate(date)" > {{ date.day }} </span> </div> </div> </template> <script> import { ref, computed, onMounted } from "vue"; import dayjs from "dayjs"; export default { setup() { const currentDate = ref(dayjs()); const selectedDate = ref(null); const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const daysInMonth = () => currentDate.value.daysInMonth(); const startDayOfMonth = () => dayjs(currentDate.value).startOf("month").day(); const dates = computed(() => { let firstWeekOffset = startDayOfMonth(); // 获取当月第一天是星期几 let totalDates = []; for (let i = 0; i < firstWeekOffset; i++) { totalDates.push({ month: null, year: null, day: "", }); } for (let d = 1; d <= daysInMonth(); d++) { totalDates.push({ month: currentDate.value.month(), year: currentDate.value.year(), day: d, }); } return totalDates; }); const selectDate = (dateObj) => { if (!dateObj || !dateObj.day) return; selectedDate.value = dayjs(`${currentDate.value.year()}-${currentDate.value.month()+1}-${dateObj.day}`); }; const isSelected = (dateObj) => { if ( selectedDate.value && selectedDate.value.date() === dateObj.day && selectedDate.value.month() === currentDate.value.month() ) { return true; } return false; }; const isToday = (dateObj) => { const now = dayjs(); return ( now.date() === dateObj.day && now.month() === currentDate.value.month() && now.year() === currentDate.value.year() ); }; const currentMonth = computed(() => currentDate.value.format("MMMM")); const currentYear = computed(() => currentDate.value.format("YYYY")); return { weekdays, dates, currentMonth, currentYear, selectDate, isSelected, isToday, }; }, }; </script> <style scoped> .calendar h2 { text-align: center; } .weekdays span { display: inline-block; width: calc(100% / 7); text-align: center; } .dates-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; } .dates-grid span { padding: 8px; border-radius: 50%; cursor: pointer; } .selected { background-color: lightblue; } .today { font-weight: bold; } </style> ``` #### 主应用入口文件 (main.js) 将上述组件引入到主应用程序中,并挂载至 DOM 节点上。 ```javascript import { createApp } from "vue"; import App from "./App.vue"; import Calendar from "./components/Calendar.vue"; const app = createApp(App); app.component("Calendar", Calendar); app.mount("#app"); ``` 此代码片段展示了一个基础版本的日历组件,支持显示当前月份以及选中的日期标记等功能[^2]。 --- ####
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值