前端开发攻略---封装日历calendar组件(纯手搓),可以根据您的需求任意改变,可玩性强

news2025/1/16 8:19:16

1、演示

2、代码

<template>
  <div class="box" v-if="startMonth.year">
    <div class="left">
      <div class="top">
        <span class="iconfont" @click="changeMonth(-1)">左</span>
        <span>{{ startMonth.year }}年{{ startMonth.month }}月</span>
        <span></span>
      </div>
      <div class="calendarMain">
        <div class="weeks">
          <span>日</span>
          <span>一</span>
          <span>二</span>
          <span>三</span>
          <span>四</span>
          <span>五</span>
          <span>六</span>
        </div>
        <div class="days">
          <div
            class="day"
            v-for="item in startMonth.dates"
            :class="monthDaysClass(item)"
            :style="[{ '--ml': item.week }]"
            @mouseenter="dayMouseMove(item)"
            @click="dayMouseClick(item)"
          >
            {{ item.day }}
          </div>
        </div>
      </div>
    </div>
    <div class="right">
      <div class="top">
        <span></span>
        <span>{{ endMonth.year }}年{{ endMonth.month }}月</span>
        <span class="iconfont" @click="changeMonth(1)">右</span>
      </div>
      <div class="calendarMain">
        <div class="weeks">
          <span>日</span>
          <span>一</span>
          <span>二</span>
          <span>三</span>
          <span>四</span>
          <span>五</span>
          <span>六</span>
        </div>
        <div class="days">
          <div
            class="day"
            v-for="item in endMonth.dates"
            :class="monthDaysClass(item)"
            :style="[{ '--ml': item.week }]"
            @mouseenter="dayMouseMove(item)"
            @click="dayMouseClick(item)"
          >
            {{ item.day }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
const currentDateIndex = ref(0)
const startMonth = ref({})
const endMonth = ref({})
const selectDate = ref([])
const isMove = ref(false)

onMounted(() => {
  initCalendar()
})

const initCalendar = () => {
  getCalendarData()
  const startIndex = startMonth.value.dates.findIndex(item => !item.isTodayBefore)
  if (startIndex == startMonth.value.dates.length - 1) {
    selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd
    selectDate.value[1] = endMonth.value.dates[0].yymmdd
  } else {
    selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd
    selectDate.value[1] = startMonth.value.dates[startIndex + 1].yymmdd
  }
}

const getCalendarData = () => {
  startMonth.value = getMonthDates(currentDateIndex.value)
  endMonth.value = getMonthDates(currentDateIndex.value + 1)
}
const changeMonth = num => {
  currentDateIndex.value += num
  getCalendarData()
}

const monthDaysClass = item => {
  if (item.isTodayBefore) return 'disabled'
  if (item.yymmdd == selectDate.value[0]) return 'active'
  if (item.yymmdd == selectDate.value[1]) return 'active'
  if (getDatesBetween(selectDate.value[0], selectDate.value[1]).includes(item.yymmdd)) return 'middle'
}

const getDatesBetween = (date1, date2) => {
  let dates = []
  let currentDate = new Date(date1)
  let endDate = new Date(date2)

  while (currentDate < endDate) {
    let dateString = currentDate.toISOString().substr(0, 10)
    dates.push(dateString)
    currentDate.setDate(currentDate.getDate() + 1)
  }
  if (dates.length > 0) {
    dates.shift()
  }
  return dates
}

const dayMouseClick = item => {
  if (item.isTodayBefore) return
  let arr = [...selectDate.value]
  if (!isMove.value) {
    if (arr.length == 1) {
      arr[1] = item.yymmdd
    } else {
      arr = []
      arr[0] = item.yymmdd
    }
    isMove.value = true
  } else {
    isMove.value = false
  }
  if (arr[0] > arr[1]) {
    selectDate.value = arr.reverse()
  } else {
    selectDate.value = arr
  }
  console.log(selectDate.value)
}

const dayMouseMove = item => {
  if (item.isTodayBefore) return
  if (!isMove.value) return
  selectDate.value[1] = item.yymmdd
}

function getMonthDates(monthOffset) {
  const today = new Date()
  const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1)
  const year = targetDate.getFullYear()
  let month = targetDate.getMonth() + 1 // 月份是从0开始的,所以要加1
  month = month >= 10 ? month : '0' + month
  const firstDay = new Date(year, targetDate.getMonth(), 1)
  const lastDay = new Date(year, targetDate.getMonth() + 1, 0)
  const monthDates = []
  for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) {
    const day = d.getDate()
    const dayOfWeek = d.getDay() // 返回0到6,0代表星期日
    const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0) // 判断是否是今天之前的日期
    monthDates.push({
      day,
      week: dayOfWeek,
      isTodayBefore,
      yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`,
    })
  }
  return { year, month, dates: monthDates }
}
</script>

<style scoped lang="scss">
.box {
  width: 793px;
  height: 436px;
  box-shadow: 2px 2px 6px #0003;
  display: flex;
  justify-content: space-between;
  padding: 30px 15px;
  .left,
  .right {
    width: 46%;
    height: 100%;
    .top {
      display: flex;
      justify-content: space-between;
      font-weight: bold;
      .iconfont {
        cursor: pointer;
        user-select: none;
      }
    }
    .calendarMain {
      .weeks {
        font-weight: bold;
        margin-top: 20px;
        display: flex;
        justify-content: space-between;
        & > span {
          display: inline-block;
          width: 50px;
          height: 50px;
          line-height: 50px;
          text-align: center;
        }
      }
      .days {
        display: flex;
        flex-wrap: wrap;
        cursor: pointer;
        .day {
          width: 50px;
          height: 50px;
          height: 50px;
          text-align: center;
          line-height: 50px;
          color: #111;
          font-size: 14px;
        }
        .day:nth-child(1) {
          margin-left: calc(var(--ml) * 50px);
        }

        .disabled {
          color: #ccc;
          cursor: not-allowed;
        }
        .active {
          background-color: #266fff;
          color: #fff;
        }
        .middle {
          background-color: rgba(38, 111, 255, 0.3);
          color: #fff;
        }
      }
    }
  }
}
</style>

3、代码解释

import { ref, reactive, onMounted } from 'vue'
import { useDatesBetween } from '@/hooks/time.js'

这里引入了 Vue 中的 ref 和 reactive 函数,以及 onMounted 钩子函数,以及从 time.js 模块中引入了 useDatesBetween 函数。

const currentDateIndex = ref(0)
const startMonth = ref({})
const endMonth = ref({})
const selectDate = ref([])
const getDatesBetween = useDatesBetween()
const isMove = ref(false)

创建了一些响应式变量,包括当前日期索引 currentDateIndex,起始月份 startMonth 和结束月份 endMonth 的引用,以及选择的日期范围 selectDateuseDatesBetween 函数的引用 getDatesBetween,以及一个标志 isMove,用于标记是否在移动状态。

onMounted(() => {
  initCalendar()
})

使用 onMounted 钩子,在组件挂载后执行 initCalendar 函数。

const initCalendar = () => {
  getCalendarData()
  const startIndex = startMonth.value.dates.findIndex(item => !item.isTodayBefore)
  if (startIndex == startMonth.value.dates.length - 1) {
    selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd
    selectDate.value[1] = endMonth.value.dates[0].yymmdd
  } else {
    selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd
    selectDate.value[1] = startMonth.value.dates[startIndex + 1].yymmdd
  }
}

initCalendar 函数初始化日历,获取日历数据并设置选择的日期范围。

const getCalendarData = () => {
  startMonth.value = getMonthDates(currentDateIndex.value)
  endMonth.value = getMonthDates(currentDateIndex.value + 1)
}

getCalendarData 函数获取当前月份和下个月份的日历数据。

const changeMonth = num => {
  currentDateIndex.value += num
  getCalendarData()
}

changeMonth 函数根据给定的数字改变当前月份索引,并重新获取日历数据。

const monthDaysClass = item => {
  if (item.isTodayBefore) return 'disabled'
  if (item.yymmdd == selectDate.value[0]) return 'active'
  if (item.yymmdd == selectDate.value[1]) return 'active'
  if (getDatesBetween(selectDate.value[0], selectDate.value[1]).includes(item.yymmdd)) return 'middle'
}

monthDaysClass 函数根据日期项目返回对应的 CSS 类名,用于渲染日历中的日期。

const dayMouseClick = item => {
  if (item.isTodayBefore) return
  let arr = [...selectDate.value]
  if (!isMove.value) {
    if (arr.length == 1) {
      arr[1] = item.yymmdd
    } else {
      arr = []
      arr[0] = item.yymmdd
    }
    isMove.value = true
  } else {
    isMove.value = false
  }
  if (arr[0] > arr[1]) {
    selectDate.value = arr.reverse()
  } else {
    selectDate.value = arr
  }
  console.log(selectDate.value)
}

dayMouseClick 函数处理日期的点击事件,根据点击选择日期范围。

const dayMouseMove = item => {
  if (item.isTodayBefore) return
  if (!isMove.value) return
  selectDate.value[1] = item.yymmdd
}

dayMouseMove 函数处理鼠标移动事件,根据鼠标移动选择日期范围。

function getMonthDates(monthOffset) {
  const today = new Date()
  const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1)
  const year = targetDate.getFullYear()
  let month = targetDate.getMonth() + 1
  month = month >= 10 ? month : '0' + month
  const firstDay = new Date(year, targetDate.getMonth(), 1)
  const lastDay = new Date(year, targetDate.getMonth() + 1, 0)
  const monthDates = []
  for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) {
    const day = d.getDate()
    const dayOfWeek = d.getDay()
    const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0)
    monthDates.push({
      day,
      week: dayOfWeek,
      isTodayBefore,
      yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`,
    })
  }
  return { year, month, dates: monthDates }
}

getMonthDates 函数根据月份偏移量获取该月的日期数据,包括年份、月份以及日期数组。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1617882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JTAG访问xilinx FPGA的IDCODE

之前调试过xilinx的XVC&#xff08;Xilinx virtual cable&#xff09;&#xff0c;突然看到有人搞wifi-JTAG&#xff08;感兴趣可以参考https://github.com/kholia/xvc-esp8266&#xff09;&#xff0c;也挺有趣的。就突然想了解一下JTAG是如何运作的&#xff0c;例如器件识别&…

关于开设RT-DETR专栏及更新内容的一些说明

​ 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 专栏介绍 YOLOv9作为最新的YOLO系列模型&#xff0c;对于做目标检测的同学是必不可少的。本专栏将针对2024年最新推出的YOLOv9检测模型&#xff0…

jsp实验10 JavaBean

二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握javabean的用法。【参考课本 上机实验 5.5.1 】 三、源代码以及执行结果截图&#xff1a; 源代码&#xff1a; Fraction.java package sea.water; public class Fraction { public double numbe…

【北京迅为】《iTOP-3588开发板系统编程手册》-第19章 V4L2摄像头应用编程

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

Skill Check: Build an LLM Application using OCI Generative AI Service

Skill Check: Build an LLM Application using OCI Generative AI Service

数据清洗:异常值检测方法

异常值检测方法总结 一、基于分布1、3σ原则2、Z_score方法3、boxplot一、基于分布 1、3σ原则 3σ原则又称为拉依达法则。该法则就是先假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间的就属于异常…

贪心算法练习day.1

理论基础 贪心算法是一种常见的解决优化问题的方法&#xff0c;其基本思想就是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部的最优决策&#xff0c;以此得到全局的最优解&#xff0c;例如在十张面额不同的钞票&#xff0c;让我们…

ctfshow 每周大挑战RCE极限挑战

讨厌SQl看到这个了想来玩玩 rce1 <?phperror_reporting(0); highlight_file(__FILE__);$code $_POST[code];$code str_replace("(","括号",$code);$code str_replace(".","点",$code);eval($code);?>括号过滤点过滤&…

查找算法之分块查找

目录 前言一、查找算法预备知识二、分块查找三、总结3.1 查找性能3.2 适用场景3.3 优缺点 前言 查找算法是一种用于在数据集合中查找特定元素的算法。在计算机科学中&#xff0c;查找算法通常被用于在数组、链表、树等数据结构中查找目标元素的位置或者判断目标元素是否存在。…

【Java--数据结构】模拟实现ArrayList

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 LIst 顺序表ArrayList 顺序表优点 IList接口 ArrayList中定义要操作的数组 在MyArrayList中 重写接口方法 新增元素 在指定位置插入元素 pos不合法异常 判断和查找元素…

家用洗地机哪款好用?盘点618值得买的洗地机品牌

对于工作忙碌或家里养了宠物的很多朋友来说&#xff0c;洗地机它集合吸尘清扫湿拖的功能&#xff0c;很大程度上解放了家庭清洁劳动的繁琐&#xff0c;让人们腾出更多的时间休息&#xff0c;那么&#xff0c;市场上有很多牌子的洗地机&#xff0c;价格也各不相同&#xff0c;那…

HarmonyOS应用性能分析工具CPU Profiler的使用指南

简介 本文档介绍应用性能分析工具CPU Profiler的使用方法&#xff0c;该工具为开发者提供性能采样分析手段&#xff0c;可在不插桩情况下获取调用栈上各层函数的执行时间&#xff0c;并展示在时间轴上。 开发者可通过该工具查看TS/JS代码及NAPI代码执行过程中的时序及耗时情况…

【Java--数据结构】提升你的编程段位:泛型入门指南,一看就会!

前言 泛型是一种编程概念&#xff0c;它允许我们编写可以适用于多种数据类型的代码。通过使用泛型&#xff0c;我们可以在编译时期将具体的数据类型作为参数传递给代码&#xff0c;从而实现代码的复用和灵活性。 在传统的编程中&#xff0c;我们通常需要为不同的数据类型编写不…

web项目运行时,报了500错误(HTTP Status 500 – Internal Server Error)

web项目运行时&#xff0c;报了500错误&#xff08;HTTP Status 500 – Internal Server Error&#xff09; 文章目录 web项目运行时&#xff0c;报了500错误&#xff08;HTTP Status 500 – Internal Server Error&#xff09;前言一、 解决方法&#xff1a;Project Structure…

视频监控/视频集中存储/云存储EasyCVR视频汇聚平台如何切换主子码流?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xff0c;视频画面1、4、9、16个可选&#xff0c;支持自定义视频轮播。EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标…

赛氪网凭借教育人优势,荣获中关村高新技术企业协会会员单位称号

2023年&#xff0c;中关村科技园区管理委员会正式公布了一批新的高新技术企业协会会员单位名单&#xff0c;赛氪网荣幸成为其中一员。这一荣誉的获得&#xff0c;不仅是对赛氪网在竞赛、科技创新教育领域的充分肯定&#xff0c;也标志着赛氪网在推动高新技术发展方面迈出了坚实…

vue3+elementui-plus实现无限递归菜单

效果图 实现方式是&#xff1a;通过给定的数据结构层数来动态生成多级菜单 menu.vue<template><el-menu:default-active"activeIndex"class"el-menu-demo"mode"horizontal"select"handleSelect"background-color"#f8f…

CorelDRAW Graphics Suite 2023最新官网中文版本CDR2022免费激活下载

CorelDRAW Graphics Suite 2023最新中文版本免费激活下载作为一款矢量图形制作工具软件&#xff0c;专门为从事插画设计、广告设计、网页设计、图形编辑等设计行业推出的工具软件。界面也是非常的简洁&#xff0c;能够让用户更快了解其中的各个功能&#xff0c;功能方法一目了然…

增强现实(AR)技术的应用场景

增强现实&#xff08;AR&#xff09;技术将虚拟信息与现实世界融合&#xff0c;为用户提供更加直观、交互式的体验。AR技术具有广泛的应用前景&#xff0c;可以应用于各行各业。以下是一些AR的应用场景。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0…

wireshark RTP分析参数

主要看丢弃和Delta&#xff0c; 丢弃就是丢掉的udp包&#xff0c;所占的比率 Delta是当前udp包接收到的时间减去上一个udp包接收到的时间 根据载荷可以知道正确的delta应该是多少&#xff0c;比如G711A&#xff0c;ptime20&#xff0c;那么delta理论上应该趋近于20. 这里的de…