vue-quick-calendar—Vue超实用日历组件(带示例图,注释超详细~)

news/2024/7/10 0:54:38 标签: vue, js, html, 组件化

效果图

本日历组件支持:
1. 标记日期
2. 选中日期
3. 切换月份
4. 上月底、下月初日期显示
5. 代码复制即可使用
6. 支持npm引入,npm传送门:html" title=vue>vue-quick-calendar
本示例为PC端,预处理器使用SCSS,移动端请自行修改样式哦~
在这里插入图片描述

一、Calendar组件代码

<template>
  <section class='m-calendar' :style="dateStyle">
    <!-- 切换月份 -->
    <header class='changeMonth'>
      <span class='prepMonth' @click="changeMonth(-1)"></span>
      <h1>{{year}}{{month}}</h1>
      <span class='nextMonth' @click="changeMonth(1)"></span>
    </header>
    <ul class='dates'>
      <!-- 星期 -->
      <li class='weeks' v-for="item in weeks" :key="item">{{item}}</li>
      <!-- 日期 -->
      <li
        class='day'
        v-for="(item, i) in dates" :key="i"
        :class="{
          isPrep: item.isPrep,
          isNext: item.isNext,
          hidden: (item.isNext || item.isPrep) && !showPrepNext,
          isToday: item.date == today,
          isSelected: item.date == selectedDate,
          isMarked: markDates.includes(item.date)
        }"
        @click="clickDate(item)"
      >{{item.date == today ? '今' : item.day}}</li>
    </ul>
  </section>
</template>

<script>
const D = new Date()
const ThisYear = D.getFullYear()
const ThisMonth = D.getMonth() + 1
const today = new Date().toLocaleDateString()
export default {
  // props都为非必传
  props: {
    // 初始年月
    startYearMonth: {
      type: String,
      default () {
        return `${ThisYear}/${ThisMonth}`
        // 格式:2021-1或2020-01或者2020/1或者2020/01
      }
    },
    // 需要标记的日期数组
    markDate: {
      type: Array,
      default () {
        return []
        // 格式:['2020-01-01', '2020-02-12']
      }
    },
    // 选中的日期
    checkedDate: {
      type: String,
      default () {
        return ''
        // 格式:'2020-01-01'
      }
    },
    // 是否星期一开始,默认星期日开始
    mondayStart: {
      type: Boolean,
      default () {
        return false
      }
    },
    // 是否显示上个月和下个月日期
    showPrepNext: {
      type: Boolean,
      default () {
        return true
      }
    },
    // 日期字体颜色
    fontColor: {
      type: String,
      default () {
        return '#000'
      }
    },
    // 标记点颜色
    markColor: {
      type: String,
      default () {
        return '#ff6633'
      }
    },
    // 选中的日期字体颜色
    activeColor: {
      type: String,
      default () {
        return '#fff'
      }
    },
    // 选中的日期背景颜色
    activeBgColor: {
      type: String,
      default () {
        return '#ff6633'
      }
    }
  },
  data () {
    return {
      // 当前年
      year: ThisYear,
      // 当前月
      month: ThisMonth,
      // 今天
      today,
      // 日期数组
      dates: [],
      // 选中的日期
      selectedDate: ''
    }
  },
  computed: {
    // 标记的日期
    markDates () {
      // 获得不带0的日期,如:2021/1/1  =>  2021/1/1
      return this.markDate.map(item => this.formatDate(item))
    },
    // 星期
    weeks () {
      if (this.mondayStart) {
        return ['一', '二', '三', '四', '五', '六', '日']
      } else {
        return ['日', '一', '二', '三', '四', '五', '六']
      }
    },
    dateStyle () {
      return {
        '--font-color': this.fontColor,
        '--mark-color': this.markColor,
        '--active-color': this.activeColor,
        '--active-bg-color': this.activeBgColor
      }
    }
  },
  created () {
    this.year = new Date(this.startYearMonth).getFullYear()
    this.month = new Date(this.startYearMonth).getMonth() + 1
    // 选中的日期
    if (this.checkedDate) {
      // 获得不带0的日期,如:2021-1-1  =>  2021/1/1
      this.selectedDate = this.formatDate(this.checkedDate)
    }
    // 初始化日历
    this.initCalendar()
  },
  methods: {
    // 初始化日历
    initCalendar () {
      // ⚠️注意:new Date中的month要减1才是真正的本月月数,即本月:this.month - 1,下个月:this.month
      // ⚠️注意:下个月的第0天即为本月的最后一天
      // 上个月总天数(本月第0天日期)
      const prepMonthDays = new Date(this.year, this.month - 1, 0).getDate()
      // 上个月最后一天星期几(本月第0天星期数)
      const prepMonthEndDayWeek = new Date(this.year, this.month - 1, 0).getDay()
      // 当前月总天数(下个月第0天日期)
      const thisMonthDays = new Date(this.year, this.month, 0).getDate()
      // 当前月第一天是星期几
      const firstDayWeek = new Date(this.year, this.month - 1, 1).getDay()
      // 当前月最后一天是星期几(下个月第0天星期数)
      const thisEndDayWeek = new Date(this.year, this.month, 0).getDay()
      var dates = []
      // 需要计算的总天数
      var totalDays = firstDayWeek + thisMonthDays
      // 从星期一开始index为1,从星期天开始index为0
      var index = this.mondayStart ? 1 : 0
      // 星期一开始且本月最后一天是星期天之后,需补满最后一行
      if (this.mondayStart && thisEndDayWeek > 0) {
        totalDays += 7 - thisEndDayWeek
      } else if (!this.mondayStart && thisEndDayWeek < 6) {
        // 星期天开始且本月最后一天是星期天之前,需补满最后一行
        totalDays += 6 - thisEndDayWeek
      }
      for (index; index < totalDays; index++) {
        // 上个月月底
        if (index < firstDayWeek) {
          // 上个月天数 - 上个月最后一天星期数 + 下标 (如:31 - 5 + 0)
          const day = prepMonthDays - prepMonthEndDayWeek + index
          const date = new Date(this.year, this.month - 2, day).toLocaleDateString()
          dates.push({ isPrep: true, day, date })
        } else if (index >= firstDayWeek + thisMonthDays) {
          // 下个月月初
          // 下标 - 当前月总天数 - 当前月第一天星期数 + 1 (如:30 - 31 + 1 + 1)
          const day = index - thisMonthDays - firstDayWeek + 1
          const date = new Date(this.year, this.month, day).toLocaleDateString()
          dates.push({ isNext: true, day, date })
        } else {
          // 本月
          // 下标 - 当前月第一天星期数 + 1 (如:5 - 5 + 1)
          const day = index - firstDayWeek + 1
          const date = new Date(this.year, this.month - 1, day).toLocaleDateString()
          dates.push({ day, date })
        }
      }
      this.dates = [...dates]
    },
    // 点击日期
    clickDate ({ date, isPrep, isNext }) {
      if (isPrep || isNext) return
      this.selectedDate = date
      this.$emit('clickDate', date.replace(/\//g, '-'))
    },
    // 切换月份
    changeMonth (month) {
      this.month += month
      if (this.month === 0) {
        this.month = 12
        this.year--
      } else if (this.month === 13) {
        this.month = 1
        this.year++
      }
      this.initCalendar()
      this.$emit('changeMonth', `${this.year}-${this.month}`)
    },
    // 格式化日期
    formatDate (date) {
      // 获得不带0,且分隔符为/的日期,如:2020-01-01  =>  2021/1/1
      return new Date(date).toLocaleDateString()
    }
  }
}
</script>

<style lang='scss' scoped>
  $fontColor: var(--font-color);
  $markColor: var(--mark-color);
  $activeColor: var(--active-color);
  $activeBgColor: var(--active-bg-color);
  .m-calendar{
    max-width: 400px;
    max-height: 450px;
    border: 1px solid #054C96;
    border-radius: 8px 8px 0 0;
    padding-bottom: 20px;
    header{
      display: flex;
      align-items: center;
      justify-content: center;
      border-bottom: 1px solid #054C96;
      padding: 20px 0;
      margin-bottom: 5px;
      h1{
        margin: 0 20px;
        color: #444;
        font-size: 20px;
        font-weight: bold;
        width: 120px;
        text-align: center;
      }
      span{
        cursor: pointer;
        padding: 4px 10px;
        &::after{
          display: inline-block;
          content: '';
          width: 10px;
          height: 10px;
          border-top: 2px solid $fontColor;
        }
        &.prepMonth::after{
          border-left: 2px solid $fontColor;
          transform: rotate(-45deg);
        }
        &.nextMonth::after{
          border-right: 2px solid $fontColor;
          transform: rotate(45deg);
        }
        &:hover::after{
          border-color: $markColor;
        }
      }
    }
    ul{
      display: flex;
      flex-wrap: wrap;
      margin: 0 auto;
      padding: 0 12px;
      li{
        width: 42px;
        height: 42px;
        margin: 4px calc((100% / 7 - 42px) / 2);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        font-weight: bold;
        position: relative;
        transition: all ease .25s;
        border-radius: 6px;
        // 标记
        &::after{
          bottom: 0;
          left: 50%;
          transform: translateX(-50%);
          position: absolute;
          display: inline-block;
          content: '';
          width: 5px;
          height: 5px;
          border-radius: 50%;
        }
        // 星期
        &.weeks{
          font-size: 18px;
          color: #444;
          margin-bottom: 12px;
        }
        &.day{
          cursor: pointer;
          font-size: 20px;
          color: $fontColor;
          // 今天
          &.isToday{
            color: $markColor;
          }
          // 标记
          &.isMarked::after{
            transition: all ease .25s;
            background: $markColor;
          }
          // 选中、hover
          &:hover, &.isSelected{
            background: $activeBgColor;
            color: $activeColor;
            &:after{
              display: none;
            }
          }
          // 上个月、下个月
          &.isNext, &.isPrep{
            cursor: default;
            opacity: .3;
            &:hover{
              color: $fontColor;
              opacity: .3;
              background: transparent;
            }
          }
          // hidden
          &.hidden{
            opacity: 0;
            &:hover{
              opacity: 0;
            }
          }
        }
      }
    }
  }
</style>

二、组件使用方式

<template>
  <calendar
    showPrepNext
    startYearMonth='2021-01'
    :markDate="markDate"
    :checkedDate='checkedDate'
    @clickDate="clickDate"
    @changeMonth="changeMonth"
  />
</template>

<script>
import calendar from './calendar'
export default {
  components: {
    calendar
  },
  data () {
    return {
      markDate: ['2021/1/1', '2021-01-12', '2021-1-18', '2021-01-20'],
      // date: '2021-01-20'
      checkedDate: '2021/01/20'
    }
  },
  created () {
  },
  methods: {
    // 点击日期
    clickDate (date) {
      console.log(date)
    },
    // 切换月份
    changeMonth (date) {
      console.log(date)
    }
  }
}
</script>

<style lang='scss' scoped>
</style>

觉得有用就点个赞吧!有问题给我留言哦,速回哦!❤️❤️


http://www.niftyadmin.cn/n/1410419.html

相关文章

创建完整的Vite + Vue3 + TypeScript + ESLint + prettierrc + Sass项目模版

相关网站 1. Vite中文官网&#xff1a;Vite中文官网 2. Vue3中文官网&#xff1a;Vue3中文官网 3. Element Plus官网&#xff1a;Element Plus官网 一、创建项目 安装Vite # npm npm init vitejs/app# yarn yarn create vitejs/app新建Vue3项目 # npm 6.x npm init vite…

手写简版Vue

学习了Vue之后&#xff0c;根据自己的理解&#xff0c;手写了简版Vue&#xff0c;希望对正在学习Vue源码的初学者有所帮助&#xff0c;一起加油&#xff01; 语雀访问地址&#xff1a;手写Vue HTML <!-- 声明meta&#xff0c;不然会乱码 --> <meta http-equiv"…

mysql-cluster 安装配置

mysql-cluster免编译包下载&#xff1a;https://dev.mysql.com/downloads/file/?id469881 新版本支持只把索引和用到的数据加载到内存&#xff0c;而不是老版本的整个数据库都得加到内存中 wget https://cdn.mysql.com//Downloads/MySQL-Cluster-7.5/mysql-cluster-gpl-7.5.6-…

封装uniapp全局弹窗并绑定到原型上

调用方式 // main.js中引入 import ./popup// Toast // 1、带成功图标提示框 this.$Toast(success, 提交成功&#xff01;, () > {// 提示框关闭后 }) // 2、不带任何图标提示框 this.$Toast(none, 请输入手机号, () > {// 提示框关闭后 }) // 3、带loading图标提示框 t…

oauth2-server-php-docs 概念

PHP的OAuth2服务器库 将OAuth2.0干净地安装到您的PHP应用程序中。 从GitHub 下载代码开始。 要求 这个库需要PHP 5.3.9。然而&#xff0c;有一个稳定的版本和开发分支的PHP 5.2.x-5.3.8为好。 安装 这个库遵循zend PSR-0标准。有一些自动加载器可以自动加载这个库&#xff0c;但…

uni-app/小程序自定义导航栏+下拉刷新完美解决方案(封装原生page滑动组件)

本组件优势&#xff1a; 1、使用小程序/uniapp原生page滑动&#xff0c;流畅度高于scroll-view组件 2、采用组件方式直接使用&#xff0c;只需在下拉刷新、上拉加载、加载完成时触发组件方法即可 3、包含无数据时空布局展示 4、可自定义下拉刷新、上拉加载样式 5、采用组件的双…

VMware vSphere 5.1 群集深入解析(二十五)- 关联性

第三部分 vSphere存储DRS 第七章 关联性 默认&#xff0c;存储DRS适用于虚拟机内部&#xff08;一个数据存储上的虚拟机的全部文件)关联规则&#xff08;VMDK关联&#xff09;. 配置完成数据存储群集后&#xff0c;在高级选项中允许你改变默认的虚拟机关联规则。 图147&#xf…

Vue Element UI 自定义描述列表组件

效果图 写在前面 写后台管理经常从列表点击查看详情&#xff0c;展示数据信息&#xff0c;Element UI虽然有表格组件&#xff0c;但是描述组件并没有&#xff0c;之前团队的成员遇到这种情况都自己去写样式&#xff0c;写起来也麻烦&#xff0c;而且每个人写出来的样式也不统一…