vue3实现日历日期选择(不使用任何插件,纯javaScript实现)

个人项目地址: SubTopH前端开发个人站

(自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面)

SubTopH前端开发个人站icon-default.png?t=N7T8https://subtop.gitee.io/subtoph.github.io/#/home

以上 👆 是个人前端项目,欢迎提出您的建议😊

以下是正文内容...............

实现效果

实现功能介绍:

  1. 快捷键自定义
  2. 年和月的前进后退
  3. 选中日期格式的返回,包含格式化日期和时间戳和选择类型
  4. 限制最小和最大日期的选择范围

下面是日期选择组件完整代码


<template><div class="timebox"><div class="shortcut" v-if="timeCount.length"><pv-for="(item, index) in timeCount":key="index"@click="jumpShortcut(item.timeDiff)">{{ item.title }}</p></div><!-- 日历部分--><div id="calendar"><!-- 年份  --><div class="month"><!-- 时间切换 --><div class="time-switch"><!-- 上一年 --><divclass="arrow hands iconfont icon-xiangzuo"@click="pickPre(currentYear, currentMonth, 'Y')"></div><!-- 上个月 --><divclass="arrow hands iconfont icon-xiangzuodan"@click="pickPre(currentYear, currentMonth)"></div><div class="year-month"><span class="choose-year">{{ currentYear }} 年 </span><span class="choose-month">{{ currentMonth }} 月 </span></div><!-- 下个月 --><divclass="arrow hands iconfont icon-xiangyoudan"@click="pickNext(currentYear, currentMonth)"></div><!-- 下一年 --><divclass="arrow hands iconfont icon-xiangyou"@click="pickNext(currentYear, currentMonth, 'Y')"></div></div></div><!-- 星期 --><ul class="weekdays"><li>日</li><li>一</li><li>二</li><li>三</li><li>四</li><li>五</li><li>六</li></ul><!-- 日期 --><div class="days"><!-- 核心 v-for循环 每一次循环用<li>标签创建一天 --><div v-for="dayobject in days" :key="dayobject" class="days-item"><!--不是本月day--><divv-if="dayobject.day.getMonth() + 1 != currentMonth"class="other-day":class="{prohibit:(minTime && dayobject.day.getTime() < minTime) ||(maxTime && dayobject.day.getTime() > maxTime)}"@click="getDayTime(dayobject.day)"><p>{{ dayobject.day.getDate() }}</p></div><!--本月day--><div class="current-month" v-else><divclass="item-day"@click="getDayTime(dayobject.day)":class="[newDate == formatDateYMD(dayobject.day) ? 'active' : '',{prohibit:(minTime && dayobject.day.getTime() < minTime) ||(maxTime && dayobject.day.getTime() > maxTime)}]"><p>{{ dayobject.day.getDate() }}</p></div></div></div></div></div></div>
</template><script>
import { reactive, toRefs, onBeforeMount, onMounted } from 'vue';
export default {name: '',props: {isCompletionZero: {type: Boolean,default: true,explain: '是否补齐日期中各位字符前的0',otherVal: 'false'},dateFormat: {type: String,default: 'Ch',explain: '日期拼接格式(Ch是年月日格式,或者设置拼接符号)',otherVal: '- :/'},minTime: {type: Number,default: 0,explain: '限制可切换的最小时间',otherVal: '时间戳'},maxTime: {type: Number,default: 0,explain: '限制可切换的最大时间',otherVal: '时间戳'},shortcutMenu:{type: Array,default: ()=>{return []},explain: '快捷菜单配置(title:快捷键名称,timeDiff:时间偏移量)',otherVal: `[{ title: '今天', timeDiff: 0 }{ title: '昨天', timeDiff: 偏移的时间戳 }]` },dateChange:{type: Function,explain: `获取选择的日期(format:当前选择的格式化日期,stamp:当前选择时间戳,type:'选择类型点击或者快捷')`}},setup(props,ctx) {const data = reactive({timeCount: [], //时间快捷currentDay: 1,currentMonth: 1,currentYear: 2023, //当前年currentWeek: 1,days: [],newDate: '' //当前日期});onBeforeMount(() => {});onMounted(() => {data.timeCount = props.shortcutMenudata.newDate = formatDateYMD(new Date());});// 初始日期const initData = (cur) => {// let leftcount = 0 // 存放剩余数量let date;if (cur) {date = new Date(cur);} else {const now = new Date();const d = new Date(formatDate(now.getFullYear(), now.getMonth(), 1));d.setDate(35);date = new Date(formatDate(d.getFullYear(), d.getMonth() + 1, 1));}data.currentDay = date.getDate();data.currentYear = date.getFullYear();data.currentMonth = date.getMonth() + 1;data.currentWeek = date.getDay(); // 1...6,0if (data.currentWeek === 0) {data.currentWeek = 7;}let str = formatDate(data.currentYear,data.currentMonth,data.currentDay);data.days.length = 0;// 今天是周日,放在第一行第7个位置,前面6个// 初始化本周for (let i = data.currentWeek; i >= 0; i--) {let d2 = new Date(str);d2.setDate(d2.getDate() - i);let dayobjectSelf = {}; // 用一个对象包装Date对象  以便为以后预定功能添加属性dayobjectSelf.day = d2;data.days.push(dayobjectSelf); // 将日期放入data 中的days数组 供页面渲染使用}// 其他周for (let j = 1; j < 42 - data.currentWeek; j++) {let d3 = new Date(str);d3.setDate(d3.getDate() + j);let dayobjectOther = {};dayobjectOther.day = d3;data.days.push(dayobjectOther);}// 下面方法对多余天数进行截取处理// dayListHandle();};// 对天数数据进行多余截取const dayListHandle = () => {// 判断当前日历中是不是包含今天,控制是否显示跳转今日的按键const currentY = new Date().getFullYear();const currentM = new Date().getMonth() + 1;data.showTodayBtn =data.currentYear !== currentY || data.currentMonth !== currentM;// 下面处理多余其他月的天数let frontNum = 0; //前let afterNum = 0; //后data.days.forEach((item, index) => {// 每一项的月份const inCurrentMonth = item.day.getMonth() + 1;const halfLength = data.days.length / 2;if (data.currentMonth === inCurrentMonth) return; // 和当前月份相等就不在执行if (index < halfLength) {frontNum++;} else {afterNum++;}});if (afterNum < 7 && frontNum < 7) return;if (afterNum > 6) {// console.log('后截取');data.days = data.days.splice(0, data.days.length - 7);}if (frontNum > 6) {// console.log('前截取');data.days = data.days.splice(7);}// 最终展示的天数 42、35、28三个数量// console.log(data.days);};// 快捷跳转// timeDiff  正负 前后移动的时间戳msconst jumpShortcut = (timeDiff) => {let thenTime = new Date();// 计算需要跳转的时间戳thenTime.setTime(thenTime.getTime() + timeDiff);if ((props.minTime && thenTime.getTime() < props.minTime) ||(props.maxTime && thenTime.getTime() > props.maxTime))return;// 根据跳转的时间切换日历const currentY = thenTime.getFullYear();const currentM = thenTime.getMonth() + 1;pickNext(currentY, currentM - 1);data.newDate = formatDateYMD(thenTime); //例如:2023年2月23日console.log('快捷', data.newDate);console.log('快捷时间戳', thenTime.getTime());ctx.emit('dateChange',{format:data.newDate,stamp:thenTime.getTime(),type:'shortcut'})};// 点击日期const getDayTime = (el) => {let timeMs = el.getTime();// 判断时间选择控制范围if ((props.minTime && timeMs < props.minTime) ||(props.maxTime && timeMs > props.maxTime))return;data.newDate = formatDateYMD(el);// 点击其他月份直接跳转,到指定月份if (data.currentMonth < el.getMonth() + 1) {pickNext(data.currentYear, data.currentMonth);}if (data.currentMonth > el.getMonth() + 1) {pickPre(data.currentYear, data.currentMonth);}console.log('手动点击', data.newDate);console.log('手动点击时间戳', timeMs);ctx.emit('dateChange',{format:data.newDate,stamp:timeMs,type:'click'})};// 上个月const pickPre = (year, month, type) => {let m = month;let y = year;if (type === 'Y') {y -= 1;} else {if (m === 1) {y -= 1;m = 12;} else {m -= 1;}}initData(formatDate(y, m, 1));};// 下个月const pickNext = (year, month, type) => {let m = month;let y = year;if (type === 'Y') {y += 1;} else {if (m === 12) {y += 1;m = 1;} else {m += 1;}}initData(formatDate(y, m, 1));};// 返回 类似 2022-05-17 格式的字符串const formatDate = (year, month, day) => {let y = year;let m = month;if (m < 10) m = '0' + m;let d = day;if (d < 10) d = '0' + d;return y + '-' + m + '-' + d;};// 日期格式化,个位数不增加0,2023年2月7日const formatDateYMD = (date) => {let y = date.getFullYear();let m = date.getMonth() + 1;let d = date.getDate();// 是否补齐0if (props.isCompletionZero) {m = m < 10 ? '0' + m : m;d = d < 10 ? '0' + d : d;}// 年月日拼接符号// 默认年月日let returnDate = `${y}年${m}月${d}日`;if (props.dateFormat !== 'Ch') {// 符号拼接const symbol = props.dateFormat;returnDate = `${y}${symbol}${m}${symbol}${d}`;}return returnDate;};// 判断是不是今天const isToday = (day) => {return (day.getFullYear() == new Date().getFullYear() &&day.getMonth() == new Date().getMonth() &&day.getDate() == new Date().getDate());};initData(null);return {jumpShortcut,pickPre,pickNext,getDayTime,isToday,formatDateYMD,...toRefs(data)};}
};
</script>
<style scoped lang="less">
.timebox {// width: 600px;background: #fff;overflow: hidden;display: flex;.shortcut {width: 100px;font-size: 14px;line-height: 26px;padding: 10px;box-sizing: border-box;color: #606266;border-right: 1px solid #ddd;cursor: pointer;transition: 0.3s;p {&:hover {transform: scale(1.05);color: @TSB;}}}#calendar {width: 320px;padding: 10px 20px;cursor: pointer;background: #fff;border-radius: 5px;.month {width: 100%;font-size: 16px;overflow: hidden;.time-switch {display: flex;height: 26px;line-height: 26px;.year-month {flex: 4;text-align: center;width: 200px;color: #606266;}.arrow {flex: 1;font-size: 14px;text-align: center;opacity: 0.5;&:hover {opacity: 1;}}}}.weekdays {overflow: hidden;padding: 10px 0;line-height: 20px;border-bottom: 1px solid @HSE;display: flex;justify-content: space-between;flex-wrap: wrap;li {width: 14%;float: left;text-align: center;color: #606266;font-size: 12px;}}.days {overflow: hidden;display: flex;justify-content: space-between;flex-wrap: wrap;.days-item {width: 14%;height: 40px;float: left;text-align: center;color: #606266;box-sizing: border-box;font-size: 12px;transition: 0.3s;line-height: 40px;position: relative;&:hover {color: @TSB;}.item-day,.other-day {height: 100%;box-sizing: border-box;transition: 0.3s;height: 30px;line-height: 30px;margin-top: 5px;&.active {p {margin: 0 auto;width: 30px;border-radius: 20px;background: @TSB;color: #fff;}}&.prohibit {background: @TSD;color: @HSD;}}.current-month {height: 100%;}.other-day {color: @HSD;}}}}
}
</style>

上面代码直接创建vue文件,在其他文件直接引入组件使用即可

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3、DVWA——CSRF

文章目录 一、CSRF概述二、low2.1 通关思路2.2 源码分析 三、medium3.1 通关思路3.2 源码分析 四、high4.1 通关思路4.2 源码分析 五、impossible 一、CSRF概述 CSRF全称为跨站请求伪造&#xff08;Cross-site request forgery&#xff09;&#xff0c;是一种网络攻击方式&…

python实现某音自动登录+获取视频数据

前言 Dy这个东西想必大家都用过&#xff0c;而且还经常刷&#xff0c;今天就来用代码&#xff0c;获取它的视频数据 环境使用 Python 3.8 Pycharm 模块使用 requests selenium json re 一. 数据来源分析 1. 明确需求 明确采集网站以及数据内容 网址: https://www.dy.com/…

JavaWeb 文件上传和下载

目录 一、文件上传 1.文件上传和下载的使用说明 : 2.文件上传基本原理 : 3.文件上传经典案例 : 3.1 页面实现: 3.2 servlet实现 : 3.3 工具类实现 : 3.4 运行测试 : 3.5 注意事项 : 二、文件下载 1.文件下载基本原理 : 2.文件下载经典案例 : 2.1 准备工作 2.2 页面…

python 深度学习 解决遇到的报错问题3

目录 一、AttributeError: The vocab attribute was removed from KeyedVector in Gensim 4.0.0. 二、ImportError: cannot import name logsumexp 三、FutureWarning: Passing (type, 1) or 1type as a synonym of type is deprecated; in a future version of numpy, it w…

Spring MVC介绍

MVC模式是什么 MVC 模式&#xff0c;全称为 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式&#xff0c;它是一种软件架构模式&#xff0c;其目标是将软件的用户界面&#xff08;即前台页面&#xff09;和业务逻辑分离&#xff0c;使代码具有更高的可扩展…

睿趣科技:抖音开小店大概多久可以做起来

随着移动互联网的快速发展&#xff0c;社交媒体平台成为了人们分享生活、交流信息的主要渠道之一。在众多社交平台中&#xff0c;抖音以其独特的短视频形式和强大的用户粘性受到了广泛关注。近年来&#xff0c;越来越多的人通过在抖音上开设小店来实现创业梦想&#xff0c;这种…

【SpringBoot】最基础的项目架构(SpringBoot+Mybatis-plus+lombok+knife4j+hutool)

汝之观览&#xff0c;吾之幸也&#xff01; 从本文开始讲下项目中用到的一些框架和技术&#xff0c;最基本的框架使用的是SpringBoot(2.5.10)Mybatis-plus(3.5.3.2)lombok(1.18.28)knife4j(3.0.3)hutool(5.8.21),可以做到代码自动生成&#xff0c;满足最基本的增删查改。 一、新…

在R中安装TensorFlow、TensorFlow_Probability、numpy(R与Python系列第二篇)

目录 前言&#xff1a; 1-安装tensorflow库 Step1: 下载R包tensorflow Step2&#xff1a;安装TensorFlow库 Step3&#xff1a;导入R中 2-安装tensorflow_probability库 Step1&#xff1a;下载R包&#xff1a;tfprobability Step2&#xff1a;安装TensorFlow Probability …

力扣2. 两数相加

2. 两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个…

水稻叶病害数据集(目标检测,yolo使用)

1.数据集文件夹 train文件夹&#xff08;44229张&#xff09;&#xff0c;test文件夹&#xff08;4741张&#xff09;&#xff0c;valid文件夹&#xff08;6000张&#xff09; 2.train文件夹展示 labels展示 标签txt展示 data.yaml文件展示 对数据集感兴趣的可以关注最后一行…

正则表达式 之 断言详解

正则表达式的先行断言和后行断言一共有 4 种形式&#xff1a; (?pattern) 零宽正向先行断言(zero-width positive lookahead assertion)(?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)(?<pattern) 零宽正向后行断言(zero-width positive lookb…

linux的文件系统,理解一切皆文件

1. 系统文件I/O 1.1 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); pathname: 要打开或创建的目标文件 flags: 打开文件时…