Vue3時(shí)間軸組件問(wèn)題記錄(時(shí)間信息收集組件)
背景
最近公司要求新增一個(gè)大屏頁(yè)面,需要封裝一個(gè)時(shí)間信息收集組件。通過(guò)雙向綁定響應(yīng)式數(shù)據(jù),父組件監(jiān)聽(tīng)子組件對(duì)數(shù)據(jù)的操作。最終將信息作為接口參數(shù),請(qǐng)求相應(yīng)的數(shù)據(jù),渲染至頁(yè)面上。
技術(shù)棧
Vue3(3.2.45)
Element-Plus(2.2.27)
dayjs(^1.11.7)
效果

其中時(shí)間軸是對(duì)Element Plus組件庫(kù)中的Slider滑塊組件的二次封裝,使用樣式穿透自定義樣式,同時(shí)大家可以把onMounted生命周期中的代碼取消注釋?zhuān)梢宰龈嗟牟僮鳌N业牧硪黄恼乱彩菚r(shí)間軸組件的封裝,不過(guò)兩者略有區(qū)別,所以又單獨(dú)記錄一下。
代碼
父組件關(guān)鍵代碼
<!-- 時(shí)間選擇 表單 -->
<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);
// 雙向綁定 標(biāo)題時(shí)間
let titleTime = ref(null);
// 監(jiān)聽(tīng)時(shí)間表單變化 - 針對(duì)左右兩側(cè)的數(shù)據(jù)
watch(objInfo, (newVal) => {
console.log(newVal, "父組件監(jiān)聽(tīng)到變化");
});
</script>時(shí)間收集組件代碼
關(guān)鍵變量介紹
這些變量實(shí)際可以對(duì)外暴露出去,也就是讓父組件去定義,讓組件更具靈活性。
- optionsHour :每日小時(shí)節(jié)點(diǎn)配置項(xiàng)(可自行靈活配置)
- optionsDay :每周天數(shù)配置項(xiàng)(可自行靈活配置)
- LastDay :時(shí)間軸根據(jù)當(dāng)前時(shí)間和該變量往前推多少天,之后for循環(huán)會(huì)利用該變量為marks賦值,并求出步進(jìn)值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";
// 時(shí)間處理
import dayjs from "dayjs";
// 宏
let props = defineProps(["up", "tt"]);
const emits = defineEmits(["update:up", "update:tt"]);
// 定義變量
let optionsHour = reactive(["2點(diǎn)", "8點(diǎn)", "14點(diǎn)", "20點(diǎn)"]);
let optionsDay = reactive([
"過(guò)去1天",
"過(guò)去2天",
"過(guò)去3天",
"過(guò)去4天",
"過(guò)去5天",
"過(guò)去6天",
"過(guò)去7天",
]);
// 高亮
let indHour = ref(0);
let indDay = ref(0);
// 步長(zhǎng)
let stepValue = ref(0);
// slider 日期往前推多少天
let LastDay = ref(7);
// 當(dāng)前時(shí)間
let NowTime = new Date().getTime();
// slider 值
const value = ref(0);
// 標(biāo)記
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");
}
// 步進(jìn)值
stepValue.value = 100 / LastDay.value;
}
GetOneWeek();
// 函數(shù)
// 高亮
function handleHour(val) {
indHour.value = val;
}
function handleDay(val) {
indDay.value = val;
}
// 自定義 提示信息
function handleFormatTooltip(val) {
let num = Number(val.toFixed(1));
return marks[num];
}
// 監(jiān)聽(tīng)
// 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(() => {
// 無(wú)需刪除這段注釋 或許日后有用
// 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("實(shí)況");
// } else if (e.target.id == "span2") {
// console.log("預(yù)報(bào)");
// }
// });
});
</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>所遇問(wèn)題
1.由于marks(取值范圍在閉區(qū)間0-100)的賦值是根據(jù)LastDay 變量計(jì)算出來(lái)的,具體來(lái)說(shuō)是這段代碼:
Number(((100 * j) / LastDay.value).toFixed(1));(最初是沒(méi)有.toFixed(1))
這導(dǎo)致計(jì)算有可能存在很長(zhǎng)的小數(shù),由于小數(shù)的存在,又使得watchEffect當(dāng)中的
let num = Number(value.value.toFixed(1));endTime = marks[num];(最初沒(méi)有.toFixed(1))
有可能匹配不上返回 undefined。
也有嘗試使用Math中的四舍五入或向上/下取整解決,但這又導(dǎo)致滑塊對(duì)應(yīng)不上步進(jìn)值,最終采取了.toFixed(1)解決該問(wèn)題。(.toFixed()返回值是字符串)
2.代碼中的v-model綁定組件必須采用別名方式,否則無(wú)效。
到此這篇關(guān)于Vue3時(shí)間軸組件(時(shí)間信息收集組件)的文章就介紹到這了,更多相關(guān)Vue3時(shí)間軸組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Element Cascader 級(jí)聯(lián)選擇器的使用示例
這篇文章主要介紹了Element Cascader 級(jí)聯(lián)選擇器的使用示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
vue封裝一個(gè)簡(jiǎn)單的div框選時(shí)間的組件的方法
這篇文章主要介紹了vue封裝一個(gè)簡(jiǎn)單的div框選時(shí)間的組件的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
詳解如何使用vue實(shí)現(xiàn)頁(yè)面訪問(wèn)攔截
這篇文章主要為大家詳細(xì)介紹了如何使用vue實(shí)現(xiàn)頁(yè)面訪問(wèn)攔截功能,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下2023-08-08

