vue3封装时间计算-日期倒计时组件——还有XX天 & 第XX天 & 年月日时分秒星期几方法的封装 & setup语法糖完整用法之reactive, ref, onMounted,computed
效果
1-还有几天
2-第几天
需求
依照服务端返回的日期与当前日期进行对比
1、返回日期大于当前日期为 “还有XX天”
2、返回日期小于当前日期为 “第XX天”
代码
1、倒计时组件封装
1.1、时间原则计算组件
src/components/header/countDown.vue
<script lang="ts" setup>
import { ref } from 'vue'
const days = ref(3)
const condition = ref('还有/第')
const text = ref(`距2021-2022年迎峰度冬安全生产值班${condition.value}${days.value}天`)
</script>
<template>
<div class="days">
<!--1.0、封装 -->
{{ text }}
</div>
</template>
<style scoped>
.days {
color: #FFFFFF;
font-size: 20px;
position: relative;
left: 20px;
top: 50px;
width: 600px;
}
</style>
1.2、时间展示组件
效果-大屏右上角展示时间
src/components/header/date.vue
<template>
<div>{{ date }}</div>
</template>
<script lang="ts" setup>
import formatter from '@/utils/formatterData'
import { ref, onMounted } from 'vue'
const date = ref('')
onMounted(() => {
setInterval(() => {
getDate()
}, 1000)
})
const getDate = function () {
date.value = formatter(new Date())
}
</script>
<style scoped lang="scss">
</style>
1.31、时间公共方法封装-中文格式
XXXX年XX月XX日XX时XX分XX秒 星期X
src/utils/formatterData.ts
const complement = function (value:any) {
return value < 10 ? `0${value}` : value
}
export default (date:any) => {
const time = new Date(date)
const year = time.getFullYear()
const month = complement(time.getMonth() + 1)
const day = complement(time.getDate())
const hour = complement(time.getHours())
const minute = complement(time.getMinutes())
const second = complement(time.getSeconds())
const week = '星期' + '日一二三四五六'.charAt(time.getDay())
return `${year}年-${month}-月${day}日 ${week} ${hour}:${minute}:${second}`
}
1.32、时间公共方法封装-数字格式
XXXX年XX月XX日XX时XX分XX秒 【数字格式】
src/utils/date.ts
/*
* @Author: CL
* @Date: 2021-10-21 14:09:07
* @LastEditTime: 2021-10-21 16:28:55
* 工具方法
*/
export const formatTime = (data: Date | string, flag: boolean) => {
const date = new Date(data)
const y = date.getFullYear()
let m: number | string = date.getMonth() + 1
m = m < 10 ? ('0' + m) : m
let d : number | string = date.getDate()
d = d < 10 ? ('0' + d) : d
let h : number | string = date.getHours()
h = h < 10 ? ('0' + h) : h
let minute : number | string = date.getMinutes()
minute = minute < 10 ? ('0' + minute) : minute
let second: number | string = date.getSeconds()
second = second < 10 ? ('0' + second) : second
if (flag) {
return y + '-' + m + '-' + d
}
return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second
}
2、页面引用
效果-倒计时完整版
index.vue
<script setup lang="ts">
import { reactive, ref, computed, onMounted } from 'vue'
import { getPowerProgress, getMatchByDate } from '@/api/olympicWinter/left/Match_Schedule.ts'
// 2.0、引用
import countDown from '@/components/header/countDown.vue'
import date from '@/components/header/date.vue'
import { formatTime } from '@/utils/utils'
// 生成 0-n的数字
const indexMethod = (index: any) => {
return index + 1
}
// interface propType {
// type: string
// }
// const props = defineProps<propType>()
const props = { type: '01' }
// 时间列表数据
const listData = reactive([
{
time: '02',
today: '周三',
date: '2022-02-02'
},
{
time: '03',
today: '周四',
date: '2022-02-03'
}
])
const lists = reactive([
{
id: '',
lineName: '',
toStation: ''
}
])
// 滑动的元素
const scrollcontent = ref()
const totleactive = ref(0)
// interface 描述一个对象或者函数
interface matchInfo {
competitionDate: string
competitionEvent: string
}
const matchList = ref<Array<matchInfo>>([])
/**
* 根据日期得到结果
*/
const getMatch = async (date: string) => {
const res = await getMatchByDate(date)
matchList.value = res
}
/**
* 点击某个日期触发
*/
const totleMethod = (data: any, index: number) => {
totleactive.value = index
getMatch(data.date)
}
/**
* 获取接口数据
*/
const getProgress = async () => {
const time = new Date()
const nowTime = formatTime(time, true)
const res = await getPowerProgress(nowTime)
console.log(res, 'kkkk')
const temp = listData.filter((item: any) => {
return item.date === nowTime
})
if (temp.length <= 0) {
// 不在期限里, 就取数组的第一个
getMatch(listData[0].date)
} else {
getMatch(nowTime)
}
}
onMounted(() => {
getProgress()
})
const textType = computed(() => {
let text = ''
if (props.type === '01') {
text = '线路名称'
} else if (props.type === '02') {
text = '变电站名称'
} else if (props.type === '03') {
text = '用户名称'
}
return text
})
</script>
<template>
<div class="main-container">
<div class="head-container">
<div class="head-content">
<!--3.0、使用 -->
<countDown />
<div class="left" />
<div class="main">
<div class="left" />
<div class="right">
<date />
</div>
</div>
</div>
<div class="count">
<!-- 3.1、ref和interface 定义变量的用法 -->
<div
v-for="(item, index) in matchList"
:key="index"
>
<span>{{ item.competitionDate }}</span>
<span>{{ item.competitionEvent }}</span>
</div>
<!-- 3.2、reactive 定义变量的用法 -->
<div
class="scroll"
ref="scrollcontent"
>
<div
:class="[totleactive === index ? 'totleactive' : 'totlebox']"
v-for="(item,index) in listData"
:key="index"
@click="totleMethod(item, index)"
>
<p>{{ item.time }}</p>
<p>{{ item.today }}</p>
</div>
</div>
<!-- 3.3、onMounted 定义变量的用法 -->
<div>
<span>{{ textType }}</span>
</div>
<div>
<el-table :data="lists">
<el-table-column
prop="lineName"
label="序号"
type="index"
align="center"
width="80"
:index="indexMethod"
/>
<el-table-column
prop="toStation"
align="center"
:label="textType"
/>
</el-table>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.main-container {
width: 7660px;
height: 1070px;
display: grid;
padding: 0 10px 10px;
grid-gap: 0 10px;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 120px auto;
background-image: url(@/assets/yfdd-bs/bg.png);
background-size: cover;
color: #fff;
font-size: 23px;
grid-template-areas:
"a a a a"
"b c c d";
.head-container {
height: 120px;
background-image: url(@/assets/yfdd-bs/title.png);
background-repeat: no-repeat;
background-size: contain;
background-position: center;
grid-area: a;
display: flex;
}
.head-content {
width: 100%;
display: flex;
background-repeat: no-repeat;
background-size: contain;
background-position: center;
cursor: pointer;
.left {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-left: -200px;
}
.main {
width: 53%;
display: flex;
justify-content: end;
}
.left {
width: 50%;
}
.right {
display: flex;
justify-content: right;
line-height: 145px;
font-size: 40px;
}
}
//时间样式
.scroll {
width: 2000px;
display: flex;
transition: all 2s;
// transform: all 1s;
// margin-left: -600px;
// justify-content: space-around;
.totlebox {
// width: 53px;
// height: 53px;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0 29px;
padding: 4px;
margin-top: 5px;
p {
font-size: 24px;
font-family: PingFangSC;
font-weight: bold;
color: #ffffff;
}
p:nth-child(2) {
font-size: 16px;
}
}
.totleactive {
width: 53px;
height: 53px;
display: flex;
flex-direction: column;
justify-content: center;
background: #1269c2;
border-radius: 50%;
align-items: center;
margin: 0 29px;
padding: 4px;
margin-top: 5px;
p {
font-size: 24px;
font-family: PingFangSC;
font-weight: bold;
color: #ffffff;
}
p:nth-child(2) {
font-size: 16px;
}
}
}
}
</style>
接口
src/api/olympicWinter/left/Match_Schedule.ts
/**
* 根据日期获取接口数据
* date: 日期
*/
export const getPowerProgress = (date: string) => {
return request({
method: 'GET',
url: '/bdDailyInfo/queryBDProgress',
params: {
competitionDate: date
}
})
}
/**
* 根据时间日期获取比赛数据
* date: 日期
*/
export const getMatchByDate = (date: string) => {
return request({
method: 'POST',
url: '/competitionInfo/selectAll',
data: {
competitionDate: date
}
})
}