当前位置: 代码网 > it编程>前端脚本>Vue.js > 基于vue3+TypeScript实现一个简易的Calendar组件

基于vue3+TypeScript实现一个简易的Calendar组件

2024年05月26日 Vue.js 我要评论
功能分析目前学到功能有以下几点日历的日期展示(核心组件,计算当月天数,第一天是星期几,以及上下月日期的连接)切换月份联动日期修改定位当前日期功能实现初始化使用 vue 官网提供的命令即可 npm cr

功能分析

目前学到功能有以下几点

  • 日历的日期展示(核心组件,计算当月天数,第一天是星期几,以及上下月日期的连接)
  • 切换月份联动日期修改
  • 定位当前日期

功能实现

初始化

使用 vue 官网提供的命令即可 npm create vue@latest ,要把使用 ts 的选项勾选上,因为这里我们用到了 ts

组件分析

划分了两个组件 headercomcalendarmonth

  • headercom 用于按钮切换日期展示,以及定位当前日期
  • calendarmonth 用于日期的展示

两者的父组件为 calendarcom组件

具体操作

安装 dayjs 日期库,这里使用 dayjs 提供的 api 来获取时间 npm i dayjs , 安装 sass 预处理器 npm i sass -d

我这里选择直接在 views 中新建了 calendar 文件夹,所以后面说的 calendar 文件夹 都是对应 src/views/calendar

calendar / calendarcom.vue

新建calendarcom.vue,日历组件的整体,包含了上面所说的两个子组件,具体代码如下

<template>
  <div class="calendar">
  </div>
</template>

<script setup lang="ts">

</script>

<style scoped>
@import './index.scss';
</style>

新建 calendar / index.scss 样式文件 引入样式

.calendar {
  width: 100%;
}

calendar / calendarmonth.vue

我们首先来实现在展示日期组件的功能吧,借个图来分析一下,这个组件分为 星期、日期两个部分,首先来实现下星期的渲染

<template>
  <div class="calendar-month">
    <div class="calendar-month-week-list">
      <div v-for="(item, index) in weeklist" :key="index" class="calendar-month-week-list-item">
        {{ item }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const weeklist = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
</script>

<style scoped>
@import './index.scss';
</style>

.calendar-month {
  &-week-list {
    display: flex;
    width: 100%;
    box-sizing: border-box;
    border-bottom: 1px solid #ccc;
    &-item {
      padding: 20px 16px;
      text-align: left;
      color: #7d7d7d;
      flex: 1;
    }
  }
}

父组件引入该组件

<template>
  <div>
    <calendarcom />
  </div>
</template>

<script setup lang="ts">
import calendarcom from './calendar/calendarcom.vue'
</script>

<style scoped></style>

此时日期组件的顶部就渲染好了

接下来就是日期渲染了,日期渲染就是拿到第一天的日期,计算是星期几,然后把前面空余的数据填补成上个月的末尾日期,后面超出的数据填补成下个月月初日期

这个时候有小伙伴就会问,怎么获取第一天,又怎么获取前一个月的时间和后一个月的时间呢?别着急呀,这些功能 dayjs 都给我们提供好了方法了,当然用 date 对象也可以获取这些

// test.js
import dayjs from 'dayjs'

console.log(dayjs('2024-04-29').daysinmonth()) // 30
console.log(dayjs('2024-04-29').startof('month').format('yyyy-mm-dd')) // 2024-04-01
console.log(dayjs('2024-04-29').endof('month').format('yyyy-mm-dd')) // 2024-04-30
console.log(dayjs('2024-04-29').startof('month').day()) // 1  星期一

测试这些方法,我们知道2024年4月的天数,第一天是星期几,起始日期,结束日期
我们在calendarmonth 组件中实现一个 getallday 的方法,返回一个日期数组,并且给 calendar 组件添加一个 value 属性,传入一个初始时间,获取到要展示的日期后渲染到页面即可

<!-- homeview -->
<template>
  <div>
    <calendarcom :value="dayjs('2024-05-01')" />
  </div>
</template>

<script setup lang="ts">
import dayjs from 'dayjs'
import calendarcom from './calendar/calendarcom.vue'
</script>

<style scoped></style>

<!-- calendarcom -->

<template>
  <div class="calendar">
    <calendarmonth :value="props.value" />
  </div>
</template>

<script setup lang="ts">
import type { dayjs } from 'dayjs'
import calendarmonth from './calendarmonth.vue'

export interface calendarprops {
  value: dayjs
}

const props = defineprops<calendarprops>()
</script>

<style scoped>
@import './index.scss';
</style>


<!-- calendarmonth -->
<template>
  <div class="calendar-month">
    <!-- 星期列表 -->
    <div class="calendar-month-week-list">
      <div v-for="(item, index) in weeklist" :key="index" class="calendar-month-week-list-item">
        {{ item }}
      </div>
    </div>

    <!-- 日期展示 -->
    <div class="calendar-month-body">
      <div class="calendar-month-body-row">
        <div
          :class="[
            'calendar-month-body-cell',
            item.currentmonth ? 'calendar-month-body-cell-current' : ''
          ]"
          v-for="(item, index) in alldays"
          :key="index"
        >
          {{ item.date.date() }}
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { dayjs } from 'dayjs'
import type { calendarprops } from './calendarcom.vue'
import { ref } from 'vue'

const weeklist = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']

interface calendarmonthprops extends calendarprops {}

const props = defineprops<calendarmonthprops>()

const { value } = props

/**
 * 获取日期数据方法
 * @param date 传入日期
 */
const getallday = (date: dayjs) => {
  const startdate = date.startof('month')
  const day = startdate.day() // 当月第一天是星期几

  const daysinfo = new array<{ date: dayjs; currentmonth: boolean }>(6 * 7)

  for (let i = 0; i < day; i++) {
    daysinfo[i] = {
      date: startdate.subtract(day - i, 'day'), // 上个月末尾几天日期
      currentmonth: false // 是否为当月日期
    }
  }

  for (let i = day; i < daysinfo.length; i++) {
    const calcdate = startdate.add(i - day, 'day')

    daysinfo[i] = {
      date: calcdate,
      currentmonth: calcdate.month() === date.month()
    }
  }

  return daysinfo
}
const alldays = ref(getallday(value))
</script>

<style scoped>
@import './index.scss';
</style>



.calendar {
  width: 100%;
}

.calendar-month {
  &-week-list {
    display: flex;
    width: 100%;
    box-sizing: border-box;
    border-bottom: 1px solid #ccc;
    &-item {
      padding: 20px 16px;
      text-align: left;
      color: #7d7d7d;
      flex: 1;
    }
  }
  &-body {
    &-row {
      display: flex;
      flex-wrap: wrap;
    }
    &-cell {
      width: calc(100% / 7);
      padding: 10px;
      height: 100px;
      box-sizing: border-box;
      border-bottom: 1px solid #ccc;
      border-right: 1px solid #ccc;
      color: #ccc;
      &-current {
        color: #000;
      }
    }
  }
}

可以看到日期已经渲染出来了

calendar / headercom.vue

日期渲染出来后,需要有操作按钮切换月份,那这一部分放在了 headercom 的组件中,让我们来实现一下

<template>
  <div class="calendar-header">
    <div class="calendar-header-left">
      <div class="calendar-header-icon"><</div>
      <div class="calendar-header-value">2024年4月</div>
      <div class="calendar-header-icon">></div>
      <button class="calendar-header-btn">今天</button>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style scoped>
@import './index.scss';
</style>

.calendar-header {
  &-left {
    display: flex;
    align-items: center;
    height: 40px;
  }

  &-value {
    font-size: 16px;
  }

  &-icon {
    width: 28px;
    height: 28px;

    line-height: 28px;

    border-radius: 50%;
    text-align: center;
    font-size: 12px;

    user-select: none;
    cursor: pointer;

    margin-right: 12px;
    &:not(:first-child) {
      margin: 0 12px;
    }

    &:hover {
      background: #ccc;
    }
  }

  &-btn {
    background: #eee;
    cursor: pointer;
    border: 0;
    padding: 0 15px;
    line-height: 28px;

    &:hover {
      background: #ccc;
    }
  }
}

那么到这一步,我们接下来要做的就是点击按钮后通知 calendar 组件切换对应月份并且在切换月份的同时让相应的日期对应上,那么具体实现是这样

<!-- headercom -->
<!-- headercom -->
<template>
  <div class="calendar-header">
    <div class="calendar-header-left">
      <div class="calendar-header-icon" @click="premonthhandler">&lt;</div>
      <div class="calendar-header-value">{{ curmonth.format('yyyy 年 mm 月') }}</div>
      <div class="calendar-header-icon" @click="nextmonthhandler">&gt;</div>
      <button class="calendar-header-btn" @click="todayhandler">今天</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { dayjs } from 'dayjs'

interface headerprops {
  curmonth: dayjs
}

const props = defineprops<headerprops>()

const emit = defineemits(['premonth', 'nextmonth', 'today'])

// 切换上一个月
const premonthhandler = () => {
  emit('premonth')
}

// 切换下一个月
const nextmonthhandler = () => {
  emit('nextmonth')
}

// 点击按钮获取今天日期
const todayhandler = () => {
  emit('today')
}
</script>

<style scoped>
@import './index.scss';
</style>


<!-- calendarcom -->

<template>
  <div class="calendar">
    <headercom
      @premonth="premonthhandler"
      @nextmonth="nextmonthhandler"
      :curmonth="curmonth"
      @today="todayhandler"
    />
    <calendarmonth :value="curvalue" :curmonth="curmonth" @clickcell="handleclickcell" />
  </div>
</template>

<script setup lang="ts">
import type { dayjs } from 'dayjs'
import calendarmonth from './calendarmonth.vue'
import headercom from './headercom.vue'
import { ref } from 'vue'
import dayjs from 'dayjs'

export interface calendarprops {
  value: dayjs
}

const props = defineprops<calendarprops>()

let curvalue = ref(props.value)
let curmonth = ref(props.value)

const premonthhandler = () => {
  curmonth.value = curmonth.value.subtract(1, 'month')
}

const nextmonthhandler = () => {
  curmonth.value = curmonth.value.add(1, 'month')
}

const todayhandler = () => {
  const date = dayjs(date.now())
  curmonth.value = date
  curvalue.value = date
}

const handleclickcell = (date: dayjs) => {
  curvalue.value = date
}
</script>

<style scoped>
@import './index.scss';
</style>


<!-- calendarmonth -->
<template>
  <div class="calendar-month">
    <!-- 星期列表 -->
    <div class="calendar-month-week-list">
      <div v-for="(item, index) in weeklist" :key="index" class="calendar-month-week-list-item">
        {{ item }}
      </div>
    </div>

    <!-- 日期展示 -->
    <div class="calendar-month-body">
      <div class="calendar-month-body-row">
        <div
          :class="[
            'calendar-month-body-cell',
            item.currentmonth ? 'calendar-month-body-cell-current' : ''
          ]"
          v-for="(item, index) in alldays"
          :key="index"
          @click="handleclickcell(item.date)"
        >
          <div
            :class="
              value.format('yyyy-mm-dd') === item.date.format('yyyy-mm-dd')
                ? 'calendar-month-body-cell-selected'
                : ''
            "
          >
            {{ item.date.date() }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { dayjs } from 'dayjs'
import type { calendarprops } from './calendarcom.vue'
import { ref, watch } from 'vue'

const weeklist = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']

interface calendarmonthprops extends calendarprops {
  curmonth: dayjs
}

const props = defineprops<calendarmonthprops>()

/**
 * 获取日期数据方法
 * @param date 传入日期
 */
const getallday = (date: dayjs) => {
  const startdate = date.startof('month')
  const day = startdate.day() // 当月第一天是星期几

  const daysinfo = new array<{ date: dayjs; currentmonth: boolean }>(6 * 7)

  for (let i = 0; i < day; i++) {
    daysinfo[i] = {
      date: startdate.subtract(day - i, 'day'), // 上个月末尾几天日期
      currentmonth: false // 是否为当月日期
    }
  }

  for (let i = day; i < daysinfo.length; i++) {
    const calcdate = startdate.add(i - day, 'day')

    daysinfo[i] = {
      date: calcdate,
      currentmonth: calcdate.month() === date.month()
    }
  }

  return daysinfo
}

const alldays = ref(getallday(props.curmonth))

watch(
  () => props.curmonth,
  (newv) => {
    alldays.value = getallday(newv)
  }
)

const emit = defineemits(['clickcell'])

const handleclickcell = (date: dayjs) => {
  emit('clickcell', date)
}
</script>

<style scoped>
@import './index.scss';
</style>


.calendar {
  width: 100%;
}

.calendar-month {
  &-week-list {
    display: flex;
    width: 100%;
    box-sizing: border-box;
    border-bottom: 1px solid #ccc;
    &-item {
      padding: 20px 16px;
      text-align: left;
      color: #7d7d7d;
      flex: 1;
    }
  }
  &-body {
    &-row {
      display: flex;
      flex-wrap: wrap;
    }
    &-cell {
      width: calc(100% / 7);
      padding: 10px;
      height: 100px;
      box-sizing: border-box;
      border-bottom: 1px solid #ccc;
      border-right: 1px solid #ccc;
      color: #ccc;
      &-current {
        color: #000;
      }
      &-selected {
        background: blue;
        width: 28px;
        height: 28px;
        line-height: 28px;
        text-align: center;
        color: #fff;
        border-radius: 50%;
        cursor: pointer;
      }
    }
  }
}

.calendar-header {
  &-left {
    display: flex;
    align-items: center;
    height: 40px;
  }

  &-value {
    font-size: 16px;
  }

  &-icon {
    width: 28px;
    height: 28px;

    line-height: 28px;

    border-radius: 50%;
    text-align: center;
    font-size: 12px;

    user-select: none;
    cursor: pointer;

    margin-right: 12px;
    &:not(:first-child) {
      margin: 0 12px;
    }

    &:hover {
      background: #ccc;
    }
  }

  &-btn {
    background: #eee;
    cursor: pointer;
    border: 0;
    padding: 0 15px;
    line-height: 28px;

    &:hover {
      background: #ccc;
    }
  }
}

最后实现的效果是这样

到这里,这个日历的主要核心功能差不多就实现了

小结

那么我们来回顾一下日历组件的一些核心的地方吧:

  • 首先是日期的获取,这个要熟练使用dayjs库或者date的api使用
  • 其次就是对日期的计算,当月时间的判断,获取当月上下月时间的方法
  • 切换月份重新渲染日期组件数据

总结

以上就是基于vue3+typescript实现一个简易的calendar组件的详细内容,更多关于vue3 typescript实现calendar组件的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com