当前位置: 首页 > news >正文

vue3横向滚动日期选择器组件(Element Plus)

vue横向滚动日期选择器组件

组件使用到了element-plus组件库和dayjs库,使用前先保证项目中已经下载导入

主要功能:选择日期,点击日期可以让此日期滚动到视图中间,左滑右滑同理,支持跳转至任意日期,支持自定义滚动日期的数量

组件中用到了other.ts
工具代码other.ts

importdayjsfrom'dayjs'importcalendarfrom'dayjs/plugin/calendar'import'dayjs/locale/zh-cn'dayjs.locale('zh-cn')dayjs.extend(calendar)dayjs().calendar(null,{sameDay:'[Today]',// The same day ( Today at 2:30 AM )nextDay:'[Tomorrow]',// The next day ( Tomorrow at 2:30 AM )nextWeek:'dddd',// The next week ( Sunday at 2:30 AM )lastDay:'[Yesterday]',// The day before ( Yesterday at 2:30 AM )lastWeek:'[Last]',// Last week ( Last Monday at 2:30 AM )sameElse:'DD/MM/YYYY'// Everything else ( 7/10/2011 )})functionjudegSame(dj1:dayjs.Dayjs,dj2:dayjs.Dayjs){returndj1.isSame(dj2)}functiongetRelativeTime(dj:dayjs.Dayjs){letnow=dayjs(dayjs().format('YYYY-MM-DD'))if(judegSame(now,dj)){return'今天'}now=now.add(1,'day')if(judegSame(now,dj)){return'明天'}now=now.add(1,'day')if(judegSame(now,dj)){return'后天'}letd=dj.day()constbackArr=['日','一','二','三','四','五','六']return'周'+backArr[d]}export{dayjs,getRelativeTime}

组件代码SlideDatePicker.vue

<scriptsetuplang="ts">import{ArrowLeft,ArrowRight}from'@element-plus/icons-vue';import{dayjs,getRelativeTime}from'./other';// 日期加载总量const{count}=withDefaults(defineProps<{count:number}>(),{count:30})constactiveIndex=ref(0)constdateItemRefs=ref<HTMLElement[]>([])constdateItmeWrapRef=ref<HTMLElement>()constcurDate=ref('')// 日期选择器 选择的日期constshowDateList=ref<Record<string,any>[]>([])constemit=defineEmits(['dateChange'])functioncalc(format?:string){if(!format){format=dayjs().format('YYYY-MM-DD')}showDateList.value=[]letbeforeCount=Math.floor((count+1)/2)// 上取整letafterCount=Math.floor(count/2)letcur=dayjs(dayjs(format).format('YYYY-MM-DD'))for(leti=0;i<beforeCount;i++){showDateList.value.push({date:cur.format('YYYYMMDD'),dateMd:cur.format('MMDD'),week:getRelativeTime(cur)})cur=cur.subtract(1,'day')}showDateList.value=showDateList.value.reverse()// 反转 让最早的日期排在第一位cur=dayjs(dayjs(format).format('YYYY-MM-DD'))for(leti=0;i<afterCount;i++){cur=cur.add(1,'day')showDateList.value.push({date:cur.format('YYYYMMDD'),dateMd:cur.format('MMDD'),week:getRelativeTime(cur)})}}constgetMiddle=computed(()=>{returnMath.floor((showDateList.value.length+1)/2)-1})functionmove(step:number){// 越界无效if(activeIndex.value+step>=showDateList.value.length||activeIndex.value+step<0){return}activeIndex.value+=step dateItmeWrapRef.value?.scrollTo({behavior:'smooth',left:(dateItemRefs.value[activeIndex.value].offsetLeft-dateItmeWrapRef.value.offsetWidth/2)})curDate.value=dayjs(showDateList.value[activeIndex.value].date,'YYYYMMDD').format('YYYY-MM-DD')emit('dateChange',curDate.value)}functionmoveToIndex(index:number){if(index>=showDateList.value.length||index<0){return}activeIndex.value=index dateItmeWrapRef.value?.scrollTo({behavior:'smooth',left:(dateItemRefs.value[activeIndex.value].offsetLeft-dateItmeWrapRef.value.offsetWidth/2)})curDate.value=dayjs(showDateList.value[activeIndex.value].date,'YYYYMMDD').format('YYYY-MM-DD')emit('dateChange',curDate.value)}functiondatePickerChange(value:string){// 重新计算curDate.value=dayjs(value).format('YYYY-MM-DD')calc(curDate.value)activeIndex.value=getMiddle.valuenextTick(()=>{dateItmeWrapRef.value?.scrollTo({behavior:'instant',left:(dateItemRefs.value[activeIndex.value].offsetLeft-dateItmeWrapRef.value.offsetWidth/2)})})emit('dateChange',curDate.value)}onMounted(()=>{calc()activeIndex.value=getMiddle.value curDate.value=dayjs().format('YYYY-MM-DD')nextTick(()=>{dateItmeWrapRef.value?.scrollTo({behavior:'instant',left:(dateItemRefs.value[activeIndex.value].offsetLeft-dateItmeWrapRef.value.offsetWidth/2)})})})</script><template><divclass="date_picker_wrap"><divclass="left_icon"><el-button:icon="ArrowLeft"link@click="move(-1)"></el-button></div><divclass="date_item_wrap"ref="dateItmeWrapRef"><divclass="date_item":class="index === activeIndex ? 'active' : ''"v-for="(item, index) in showDateList"ref="dateItemRefs"@click="moveToIndex(index)"><span>{{ item.dateMd }}</span><span>{{ item.week }}</span></div></div><divclass="right_icon"><el-button:icon="ArrowRight"link@click="move(1)"></el-button></div><divclass="calendar_icon"><el-date-pickerstyle="width:126px;"v-model="curDate"type="date"placeholder="选择日期"format="YYYY-MM-DD":clearable="false"@change="datePickerChange"/></div></div></template><stylescoped>.date_picker_wrap{background:#fff;width:100%;height:52px;border-radius:6px;padding:4px 8px;display:flex;align-items:center;font-size:14px;color:#4b5563;.date_item_wrap{width:100%;display:flex;flex:1;overflow:hidden;.active{color:#3c6cfe;}.date_item{padding:0 6px;width:96px;height:100%;display:flex;align-items:center;justify-content:center;flex-shrink:0;border-left:solid 1px #e5e7eb;border-right:solid 1px #e5e7eb;cursor:pointer;&:hover{color:#3c6cfe;}span{padding:0 2px;}}}.left_icon, .right_icon, .calendar_icon{padding:0 8px;}}</style>

使用方式

传入count 30,组件初始化横向滚动日期数为30个,初始化数量不要太少,最好占满宽度,让其可以滚动。

<SlideDatePicker:count="30"@dateChange="dateChange"/>
functiondateChange(value:string){console.log('选中的日期',value);// 2024-12-19}

效果图

2025-07-23 测试发现如果这个组件的父div不是靠左的位置,选择到的日期无法居中,甚至会出现在视图外的情况,修正后代码如下,template和style不变

<script setup lang="ts">import{ArrowLeft,ArrowRight}from"@element-plus/icons-vue";import{dayjs,getRelativeTime}from"@/utils/index";// 日期加载总量const{count}=withDefaults(defineProps<{count:number;}>(),{count:30});constactiveIndex=ref(0);constdateItemRefs=ref<HTMLElement[]>([]);constdateItmeWrapRef=ref<HTMLElement>();constcurDate=ref("");// 日期选择器 选择的日期constshowDateList=ref<Record<string,any>[]>([]);constemit=defineEmits(["dateChange"]);functioncalc(format?:string){if(!format){format=dayjs().format("YYYY-MM-DD");}showDateList.value=[];letbeforeCount=Math.floor((count+1)/2);// 上取整letafterCount=Math.floor(count/2);letcur=dayjs(dayjs(format).format("YYYY-MM-DD"));for(leti=0;i<beforeCount;i++){showDateList.value.push({date:cur.format("YYYYMMDD"),dateMd:cur.format("MMDD"),week:getRelativeTime(cur)});cur=cur.subtract(1,"day");}showDateList.value=showDateList.value.reverse();// 反转 让最早的日期排在第一位cur=dayjs(dayjs(format).format("YYYY-MM-DD"));for(leti=0;i<afterCount;i++){cur=cur.add(1,"day");showDateList.value.push({date:cur.format("YYYYMMDD"),dateMd:cur.format("MMDD"),week:getRelativeTime(cur)});}}constgetMiddle=computed(()=>{returnMath.floor((showDateList.value.length+1)/2)-1;});functionmove(step:number){// 越界无效if(activeIndex.value+step>=showDateList.value.length||activeIndex.value+step<0){return;}activeIndex.value+=step;scrollIntoView("smooth");curDate.value=dayjs(showDateList.value[activeIndex.value].date,"YYYYMMDD").format("YYYY-MM-DD");emit("dateChange",curDate.value);}functionmoveToIndex(index:number){if(index>=showDateList.value.length||index<0){return;}activeIndex.value=index;scrollIntoView("smooth");curDate.value=dayjs(showDateList.value[activeIndex.value].date,"YYYYMMDD").format("YYYY-MM-DD");emit("dateChange",curDate.value);}functiondatePickerChange(value:string){// 重新计算curDate.value=dayjs(value).format("YYYY-MM-DD");calc(curDate.value);activeIndex.value=getMiddle.value;scrollIntoView();emit("dateChange",curDate.value);}functionscrollIntoView(behavior:ScrollBehavior="instant"){nextTick(()=>{dateItmeWrapRef.value?.scrollTo({behavior,left:dateItemRefs.value[activeIndex.value].offsetLeft-dateItmeWrapRef.value.offsetWidth/2-dateItmeWrapRef.value.offsetLeft// 如果这个组件放的div不在最靠左位置,可能出现偏差,减去当前div的偏移});});}onMounted(()=>{calc();activeIndex.value=getMiddle.value;curDate.value=dayjs().format("YYYY-MM-DD");scrollIntoView();});</script>
http://www.jsqmd.com/news/478561/

相关文章:

  • 空间函数在 ABAP SQL 里到底是什么
  • 【JEECG】JVxeTable表格行样式错位、底部滚动条错位
  • React组件更新终极指南:从setState到Fiber树的完整解析
  • 搞懂 spatial reference system:为什么 SRID 才是 SAP 空间开发里最容易被低估的基础设施
  • pt转onnx转ncnn模型(yolov8部署安卓)
  • .vscode配置文件备份
  • 搞懂 ABAP 里的 Heap 引用与 Stack 引用:从内存语义到失效边界
  • 解决protobuf版本冲突:从ImportError到streamlit顺利运行的实战指南
  • 【工具-VMware Workstation-ubuntu】
  • ProcessHacker文件锁定检测:解决应用程序文件占用问题
  • pt转onnx转rknn(yolov5部署RK3566)
  • NotebookLM:Google Labs 如何用 AI 重塑知识管理体验
  • 读懂 ABAP 中的 tag interface:从语义标记到运行时契约的设计逻辑
  • 创业者必看:150+优质平台助你快速获取种子用户
  • Xcode 16及升级 Xcode 26 编译弹窗问题、编译通过无法,编译通过打包等问题汇总
  • 深入解析JESD79-5中的模式寄存器操作:MRR与MRW实战指南
  • 读懂 ABAP 内部表的 table sharing:赋值、值传递与 copy-on-write 的底层逻辑
  • 如何在ToaruOS上畅玩经典游戏:从Pong到扫雷的完整指南
  • 每天一小时七天速成自己的AI聊天语言模型(Day 2:从运算符到循环)
  • 华为路由器NAT配置实战:从动态NAT到Easy IP的完整指南(附常见错误排查)
  • 从cloudscraper到FlareSolverr:一次攻克Cloudflare五秒盾的技术演进实录
  • Django-Oscar支付网关集成终极指南:支持多种支付方式的完整解决方案
  • 西电软工智能软件大作业实战:从选题到高分展示的全流程拆解
  • 不用翻墙!5分钟搞定PX4开发环境搭建(附百度云资源)
  • 把 Program Directives 用到位:谈透 ABAP Doc 与 Pragmas,让 ABAP 源码既清晰又可检查
  • Memos捷径(Shortcuts)用法介绍
  • KALI网络故障排查:解决DNS解析失败与网络不可达的实用指南
  • 群晖NAS部署HomeAssistant全攻略:从内网穿透到智能家居远程管理
  • IC设计转行指南:零基础如何快速掌握RTL设计与后端流程(附免费学习资源)
  • 把 ABAP CDS View Entity 的 session_variable 讲透:从 $session 语法到 Clean Core 设计实践