欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于vue3+TypeScript實現(xiàn)一個簡易的Calendar組件

 更新時間:2024年05月01日 07:50:29   作者:愛泡澡的小蘿卜  
最近在學(xué)習(xí) react,在學(xué)習(xí)到使用 react 開發(fā) Calendar 組件的時候,突然聯(lián)想到使用 vue 進(jìn)行 Calendar 功能的實現(xiàn),因為目前使用的技術(shù)棧是 vue,剛好可以加深下對 vue3 和 ts 的使用印象,所以本文給大家介紹了基于vue3+TypeScript實現(xiàn)一個簡易的Calendar組件

功能分析

目前學(xué)到功能有以下幾點

  • 日歷的日期展示(核心組件,計算當(dāng)月天數(shù),第一天是星期幾,以及上下月日期的連接)
  • 切換月份聯(lián)動日期修改
  • 定位當(dāng)前日期

功能實現(xiàn)

初始化

使用 vue 官網(wǎng)提供的命令即可 npm create vue@latest ,要把使用 ts 的選項勾選上,因為這里我們用到了 ts

組件分析

劃分了兩個組件 HeaderComCalendarMonth

  • HeaderCom 用于按鈕切換日期展示,以及定位當(dāng)前日期
  • CalendarMonth 用于日期的展示

兩者的父組件為 CalendarCom組件

具體操作

安裝 dayjs 日期庫,這里使用 dayjs 提供的 api 來獲取時間 npm i dayjs , 安裝 sass 預(yù)處理器 npm i sass -D

我這里選擇直接在 views 中新建了 Calendar 文件夾,所以后面說的 Calendar 文件夾 都是對應(yīng) 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

我們首先來實現(xiàn)在展示日期組件的功能吧,借個圖來分析一下,這個組件分為 星期、日期兩個部分,首先來實現(xiàn)下星期的渲染

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

此時日期組件的頂部就渲染好了

接下來就是日期渲染了,日期渲染就是拿到第一天的日期,計算是星期幾,然后把前面空余的數(shù)據(jù)填補成上個月的末尾日期,后面超出的數(shù)據(jù)填補成下個月月初日期

這個時候有小伙伴就會問,怎么獲取第一天,又怎么獲取前一個月的時間和后一個月的時間呢?別著急呀,這些功能 dayjs 都給我們提供好了方法了,當(dāng)然用 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月的天數(shù),第一天是星期幾,起始日期,結(jié)束日期
我們在CalendarMonth 組件中實現(xiàn)一個 getAllDay 的方法,返回一個日期數(shù)組,并且給 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

/**
 * 獲取日期數(shù)據(jù)方法
 * @param date 傳入日期
 */
const getAllDay = (date: Dayjs) => {
  const startDate = date.startOf('month')
  const day = startDate.day() // 當(dāng)月第一天是星期幾

  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 // 是否為當(dāng)月日期
    }
  }

  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;
      }
    }
  }
}

可以看到日期已經(jīng)渲染出來了

Calendar / HeaderCom.vue

日期渲染出來后,需要有操作按鈕切換月份,那這一部分放在了 HeaderCom 的組件中,讓我們來實現(xiàn)一下

<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 組件切換對應(yīng)月份并且在切換月份的同時讓相應(yīng)的日期對應(yīng)上,那么具體實現(xiàn)是這樣

<!-- 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>()

/**
 * 獲取日期數(shù)據(jù)方法
 * @param date 傳入日期
 */
const getAllDay = (date: Dayjs) => {
  const startDate = date.startOf('month')
  const day = startDate.day() // 當(dāng)月第一天是星期幾

  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 // 是否為當(dāng)月日期
    }
  }

  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;
    }
  }
}

最后實現(xiàn)的效果是這樣

到這里,這個日歷的主要核心功能差不多就實現(xiàn)了

小結(jié)

那么我們來回顧一下日歷組件的一些核心的地方吧:

  • 首先是日期的獲取,這個要熟練使用dayjs庫或者Date的api使用
  • 其次就是對日期的計算,當(dāng)月時間的判斷,獲取當(dāng)月上下月時間的方法
  • 切換月份重新渲染日期組件數(shù)據(jù)

總結(jié)

以上就是基于vue3+TypeScript實現(xiàn)一個簡易的Calendar組件的詳細(xì)內(nèi)容,更多關(guān)于vue3 TypeScript實現(xiàn)Calendar組件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue3+vant4實現(xiàn)pdf文件上傳與預(yù)覽組件

    vue3+vant4實現(xiàn)pdf文件上傳與預(yù)覽組件

    這篇文章主要介紹了vue3如何結(jié)合vant4實現(xiàn)簡單的pdf文件上傳與預(yù)覽組件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • vue使用一些外部插件及樣式的配置代碼

    vue使用一些外部插件及樣式的配置代碼

    這篇文章主要介紹了vue使用一些外部插件及樣式的配置代碼,本文分步驟實例圖文相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-11-11
  • Vue2實現(xiàn)自適應(yīng)屏幕大小的兩種方法詳解

    Vue2實現(xiàn)自適應(yīng)屏幕大小的兩種方法詳解

    這篇文章主要為大家詳細(xì)介紹了Vue2實現(xiàn)自適應(yīng)屏幕大小的兩種方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • 詳解vue-class遷移vite的一次踩坑記錄

    詳解vue-class遷移vite的一次踩坑記錄

    本文主要介紹了vue-class遷移vite的一次踩坑記錄,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • vue3項目目錄結(jié)構(gòu)示例詳解

    vue3項目目錄結(jié)構(gòu)示例詳解

    更好的了解項目的目錄結(jié)構(gòu),能更好的去開發(fā)項目,下面這篇文章主要給大家介紹了關(guān)于vue3項目目錄結(jié)構(gòu)的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Element?plus?表單中下拉框的空值問題解決

    Element?plus?表單中下拉框的空值問題解決

    有時候會碰到查詢條件需要用下拉框來區(qū)分的時候,比如需要區(qū)分全部?|?啟用?|?停用三個狀態(tài),這時一般會給全部的value值設(shè)置為'',但是這樣會導(dǎo)致下拉框無法選中對應(yīng)的全部選項,本文就來介紹一下這個問題解決,感興趣的可以了解一下
    2024-11-11
  • vue3如何使用setup代替created

    vue3如何使用setup代替created

    Vue3中的setup是一個新的生命周期函數(shù),它可以用來代替組件中的 data和一些生命周期函數(shù)(如created和beforeMount),這篇文章主要介紹了vue3如何使用setup代替created的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • vue?長列表數(shù)據(jù)刷新的實現(xiàn)及思考

    vue?長列表數(shù)據(jù)刷新的實現(xiàn)及思考

    這篇文章主要為大家介紹了vue?長列表數(shù)據(jù)刷新的實現(xiàn)及思考,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • vue如何實時往數(shù)組里追加數(shù)據(jù)

    vue如何實時往數(shù)組里追加數(shù)據(jù)

    這篇文章主要介紹了vue如何實時往數(shù)組里追加數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue3使用ref與reactive創(chuàng)建響應(yīng)式對象的示例代碼

    Vue3使用ref與reactive創(chuàng)建響應(yīng)式對象的示例代碼

    這篇文章主要詳細(xì)介紹了Vue3使用ref與reactive創(chuàng)建響應(yīng)式對象的方法步驟,文中通過代碼示例和圖文結(jié)合的方式給大家介紹的非常詳細(xì),具有一定的參考價值,需要的朋友可以參考下
    2024-02-02

最新評論