Vue3时间轴组件问题记录(时间信息收集组件)
作者:博客风气调查员
背景
最近公司要求新增一个大屏页面,需要封装一个时间信息收集组件。通过双向绑定响应式数据,父组件监听子组件对数据的操作。最终将信息作为接口参数,请求相应的数据,渲染至页面上。
技术栈
Vue3(3.2.45)
Element-Plus(2.2.27)
dayjs(^1.11.7)
效果
其中时间轴是对Element Plus组件库中的Slider滑块组件的二次封装,使用样式穿透自定义样式,同时大家可以把onMounted生命周期中的代码取消注释,可以做更多的操作。我的另一篇文章也是时间轴组件的封装,不过两者略有区别,所以又单独记录一下。
代码
父组件关键代码
<!-- 时间选择 表单 --> <h1 class="titleH2"> {{ titleTime[0] }} - {{ titleTime[1] }} <br /> </h1> <div class="contentCenter_bottom"> <TimeForm v-model:up="objInfo" v-model:tt="titleTime"></TimeForm> </div> <script setup> // 双向绑定 obj let objInfo = ref(null); // 双向绑定 标题时间 let titleTime = ref(null); // 监听时间表单变化 - 针对左右两侧的数据 watch(objInfo, (newVal) => { console.log(newVal, "父组件监听到变化"); }); </script>
时间收集组件代码
关键变量介绍
这些变量实际可以对外暴露出去,也就是让父组件去定义,让组件更具灵活性。
- optionsHour :每日小时节点配置项(可自行灵活配置)
- optionsDay :每周天数配置项(可自行灵活配置)
- LastDay :时间轴根据当前时间和该变量往前推多少天,之后for循环会利用该变量为marks赋值,并求出步进值stepValue 。
<template> <div id="TimeForm"> <div class="top"> <div class="topContent"> <div class="topContent_left"> <el-button v-for="(item, index) in optionsHour" :key="index" size="small" :type="indHour == index ? 'primary' : ''" style=" font-family: 'DIGIB'; font-size: 15px; border-radius: 6px 6px 0px 0px; " @click="handleHour(index)" >{{ item }}</el-button > </div> <el-divider direction="vertical" style="height: 80%" /> <div class="topContent_right"> <el-button v-for="(item, index) in optionsDay" :key="index" size="small" :type="indDay == index ? 'primary' : ''" style=" font-family: 'DIGIB'; letter-spacing: 1px; font-size: 15px; border-radius: 6px 6px 0px 0px; padding-left: 7px; padding-right: 7px; " @click="handleDay(index)" >{{ item }}</el-button > </div> </div> </div> <div class="bottom"> <el-slider v-model="value" :marks="marks" :step="stepValue" :show-tooltip="true" :format-tooltip="handleFormatTooltip" /> </div> </div> </template> <script setup> // vue import { ref, reactive, watch, watchEffect, computed, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, defineProps, defineEmits, } from "vue"; // 时间处理 import dayjs from "dayjs"; // 宏 let props = defineProps(["up", "tt"]); const emits = defineEmits(["update:up", "update:tt"]); // 定义变量 let optionsHour = reactive(["2点", "8点", "14点", "20点"]); let optionsDay = reactive([ "过去1天", "过去2天", "过去3天", "过去4天", "过去5天", "过去6天", "过去7天", ]); // 高亮 let indHour = ref(0); let indDay = ref(0); // 步长 let stepValue = ref(0); // slider 日期往前推多少天 let LastDay = ref(7); // 当前时间 let NowTime = new Date().getTime(); // slider 值 const value = ref(0); // 标记 const marks = reactive({}); // 方法 function GetOneWeek() { // 为marks赋值 for (let i = LastDay.value, j = 0; i >= 0; i--, j++) { let time = new Date(NowTime - i * 24 * 60 * 60 * 1000).getTime(); let t = dayjs(time).format("YYYY-MM-DD"); let num = Number(((100 * j) / LastDay.value).toFixed(1)); // console.log(num); marks[num] = `${t}`; // console.log(i, j, "ij"); } // 步进值 stepValue.value = 100 / LastDay.value; } GetOneWeek(); // 函数 // 高亮 function handleHour(val) { indHour.value = val; } function handleDay(val) { indDay.value = val; } // 自定义 提示信息 function handleFormatTooltip(val) { let num = Number(val.toFixed(1)); return marks[num]; } // 监听 // watch([indHour, indDay, value],([newHour,newDay,newValue],[oldHour,oldDay,oldValue])=>{ // console.log(newHour,newDay,newValue); // emits("update:objInfo", !props.objInfo); // }) watchEffect(() => { let hour, endTime, hourMinuteSecond; let num = Number(value.value.toFixed(1)); switch (indHour.value) { case 0: hourMinuteSecond = 2; break; case 1: hourMinuteSecond = 8; break; case 2: hourMinuteSecond = 14; break; case 3: hourMinuteSecond = 20; break; } switch (indDay.value) { case 0: hour = (indDay.value + 1) * 24; break; case 1: hour = (indDay.value + 1) * 24; break; case 2: hour = (indDay.value + 1) * 24; break; case 3: hour = (indDay.value + 1) * 24; break; case 4: hour = (indDay.value + 1) * 24; break; case 5: hour = (indDay.value + 1) * 24; break; case 6: hour = (indDay.value + 1) * 24; break; } // console.log(marks,value.value); endTime = marks[num]; let obj = { hour: hour, hourMinuteSecond: hourMinuteSecond, endTime: endTime, }; // console.log(marks[0],marks[100]); let timeStart = marks[0].replace(/-/, '年').replace(/-/, '月') + '日'; let timeEnd = marks[100].replace(/-/, '年').replace(/-/, '月') + '日'; console.log(timeStart); let timeObj = [timeStart,timeEnd] emits("update:up", obj); emits("update:tt", timeObj); }); // 生命周期 onBeforeMount(() => {}); onMounted(() => { // 无需删除这段注释 或许日后有用 // document.querySelector(".el-slider__bar").innerHTML = ` // <div // style="width:50px; height:24px;background:#ccc; position: absolute;right: 0px;top: -34px;transform: translateX(50%); display: flex; justify-content: space-between;" // > // <span id='span1'>1</span><span id='span2'>2</span> // </div> // `; // document.querySelector(".bottom").addEventListener("click", (e) => { // if (e.target.id == "span1") { // console.log("实况"); // } else if (e.target.id == "span2") { // console.log("预报"); // } // }); }); </script> <style lang="scss" scoped> // 变量 $timeform-height: 110px; $top-height: 30px; #TimeForm { width: 100%; min-height: $timeform-height; max-height: $timeform-height; box-sizing: border-box; // border: 1px dashed rgba(255, 255, 255, 0.5); // background-color: white; display: flex; flex-direction: column; justify-content: space-between; align-items: center; user-select: none; .top { width: 100%; height: $top-height; padding: 0px 20px; .topContent { width: auto; height: 100%; border-radius: 3px 3px 0px 0px; backdrop-filter: blur(10px); /* 背景模糊效果 */ background-color: rgba(255, 255, 255, 0.5); display: flex; justify-content: flex-start; align-items: end; padding: 0px 10px; .topContent_left { width: 260px; height: 80%; display: flex; justify-content: space-around; align-items: end; } .topContent_right { width: auto; height: 80%; display: flex; justify-content: space-around; align-items: end; flex-wrap: wrap; } } } .bottom { width: 100%; height: calc($timeform-height - $top-height); background-color: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); /* 背景模糊效果 */ display: flex; justify-content: center; align-items: center; padding: 0px 45px; } :deep(.bottom .el-slider__stop) { width: 2px; height: 14px; background-color: #005edc; border-radius: 0px; } :deep(.bottom .el-slider__bar) { background-color: transparent; // position: relative; } :deep(.bottom .el-slider__marks-text) { background-color: #e0eaf8; color: #626f80; padding-left: 2px; padding-right: 2px; } // :deep(.bottom .el-slider__button-wrapper::before){ // position: absolute; // right: 0px; // top: -17px; // transform: translateX(calc(50% - 18px)); // display: inline-block; // content: '123'; // width: 60px; // height: 20px; // background: #005edc; // z-index: 9999999; // } } </style>
所遇问题
1.由于marks(取值范围在闭区间0-100)的赋值是根据LastDay 变量计算出来的,具体来说是这段代码:
Number(((100 * j) / LastDay.value).toFixed(1));(最初是没有.toFixed(1))
这导致计算有可能存在很长的小数,由于小数的存在,又使得watchEffect当中的
let num = Number(value.value.toFixed(1));endTime = marks[num];(最初没有.toFixed(1))
有可能匹配不上返回 undefined。
也有尝试使用Math中的四舍五入或向上/下取整解决,但这又导致滑块对应不上步进值,最终采取了.toFixed(1)解决该问题。(.toFixed()返回值是字符串)
2.代码中的v-model绑定组件必须采用别名方式,否则无效。
到此这篇关于Vue3时间轴组件(时间信息收集组件)的文章就介绍到这了,更多相关Vue3时间轴组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!