动态倒计时在 Vue 3 中的实现
目录
- 1. Demo
- 2. 实战Demo
- 3. 拓展Demo
1. Demo
给一版初始的Demo,在给一版实战中的Demo
基本知识点:
- Vue 3 的响应式原理:Vue 3 使用 reactive 和 ref 创建响应式数据,数据的变化会自动触发视图更新
- setup 函数:Vue 3 引入了 Composition API,其中的 setup 函数是组件逻辑的入口
- watch 侦听器:用于侦听响应式数据的变化,在倒计时场景中可以用于监听时间的变化
- 生命周期钩子:如 onMounted 和 onUnmounted 用于在组件挂载和销毁时启动和清理倒计时
- setInterval 和 clearInterval:用于每隔一段时间执行倒计时任务
Demo:
<template><div><table><thead><tr><th>任务名称</th><th>剩余时间</th></tr></thead><tbody><tr v-for="task in tasks" :key="task.id"><td>{{ task.name }}</td><td>{{ task.remainingTime }} 秒</td></tr></tbody></table></div>
</template><script setup>
import { reactive, onMounted, onUnmounted } from 'vue';const tasks = reactive([{ id: 1, name: '任务A', remainingTime: 60 },{ id: 2, name: '任务B', remainingTime: 120 },{ id: 3, name: '任务C', remainingTime: 180 },
]);let intervalId;onMounted(() => {intervalId = setInterval(() => {tasks.forEach(task => {if (task.remainingTime > 0) {task.remainingTime -= 1;}});}, 1000);
});onUnmounted(() => {clearInterval(intervalId);
});
</script>
2. 实战Demo
本身倒计时的某一列时间是固定的,那就不需要通过后端来获取动态数据,只需要基于appointmentEndTime和当前时间来计算倒计时。对于超过当前时间的数据,可以直接显示“已结束”或其他提示
以下是基于Vue 3的倒计时实现方式,重点是如何根据appointmentEndTime与当前时间来动态更新表格的倒计时列
- appointmentEndTime 固定:你可以直接在表格数据中使用固定的时间戳。
- 倒计时逻辑:通过setInterval每秒更新一次倒计时,但如果appointmentEndTime已经过期,就不再更新。
- 无须请求后端:无需额外API请求,直接使用appointmentEndTime进行计算
<template><div><el-table :data="tableData" style="width: 100%"><!-- 审核时间列 --><el-table-columnlabel="审核时间"align="center"prop="appointmentReviewTime":formatter="dateFormatter"width="170px"/><!-- 还柜时间列 --><el-table-columnlabel="还柜时间"align="center"prop="appointmentEndTime":formatter="dateFormatter"width="170px"/><!-- 倒计时列 --><el-table-column label="倒计时" align="center" width="170px"><template #default="scope"><span>{{ formatCountdown(scope.row.appointmentEndTime) }}</span></template></el-table-column></el-table></div>
</template><script>
import { ref, onMounted, onBeforeUnmount } from 'vue';export default {setup() {// 表格数据,appointmentEndTime 是固定的时间const tableData = ref([{appointmentReviewTime: '2024-09-20 12:00:00',appointmentEndTime: '2024-09-20 14:00:00' // 固定时间},{appointmentReviewTime: '2024-09-21 15:00:00',appointmentEndTime: '2024-09-21 17:00:00' // 固定时间}]);// 倒计时格式化函数const formatCountdown = (endTime) => {const now = new Date().getTime();const endTimestamp = new Date(endTime).getTime();const remainingTime = endTimestamp - now;// 如果已过期,返回 "已结束"if (remainingTime <= 0) {return '已结束';}const hours = Math.floor(remainingTime / (1000 * 60 * 60));const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60));const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);return `${hours}小时 ${minutes}分钟 ${seconds}秒`;};// 使用 setInterval 动态更新倒计时const intervalId = ref(null);onMounted(() => {intervalId.value = setInterval(() => {// 强制触发视图更新tableData.value = [...tableData.value];}, 1000);});// 清除计时器onBeforeUnmount(() => {clearInterval(intervalId.value);});return {tableData,formatCountdown};}
};
</script>
如果不会出现自动更新视图的,需要排查下浏览器的终端是否会有输出异常
实战中的Bug:(错误信息 queryParams.value is not iterable 表示在某处尝试遍历或解构了queryParams,但是它不是可迭代的对象,确保queryParams是一个对象或数组,不能将非迭代对象进行迭代操作)
3. 拓展Demo
appointmentEndTime这个是timestamp时间,也就是设定的未来时间
但是我要已过期,或者超过当前时间的 都显示 重新预约
那么只需要在超过当前时间或已过期的情况下显示“重新预约”
- 修改 formatCountdown 函数:
在 formatCountdown 函数中添加一个返回值,用于标记该时间是否已经过期
const formatCountdown = (endTime) => {const now = new Date().getTime();const endTimestamp = new Date(endTime).getTime();const remainingTime = endTimestamp - now;// 如果已过期,返回 "已过期" 并标记为需要重新预约if (remainingTime <= 0) {return { text: '已过期', isExpired: true };}const hours = Math.floor(remainingTime / (1000 * 60 * 60));const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60));const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);// 分时秒的格式化return { text: `${hours}小时 ${minutes}分钟 ${seconds}秒`, isExpired: false };
};
- 修改模板逻辑:
使用 formatCountdown 函数的返回值来判断是否显示 “重新预约”
<el-table-column label="还柜剩余时间" align="center" width="155px"><template #default="scope"><span>{{ formatCountdown(scope.row.appointmentEndTime).text }}</span></template>
</el-table-column><el-buttonlinktype="primary"@click="showRejectionReason(scope.row.id)"v-if="scope.row.appointmentEndTime !== null && formatCountdown(scope.row.appointmentEndTime).isExpired"v-hasPermi="['dangerous:appointment-commission:query']"
>重新预约
</el-button>
为了在等于当前时间点的时候触发后端请求
可以修改倒计时的函数如下:
// 倒计时格式化函数
const formatCountdown = (endTime,id) => {const now = new Date().getTime();const endTimestamp = new Date(endTime).getTime();const remainingTime = endTimestamp - now;// 设置一个容忍时间范围(例如,1秒)const tolerance = 1000; // 1秒// 当时间接近到达时,发送请求到后端更新状态if (remainingTime <= tolerance && remainingTime > 0) {console.log(1); // 确保能够输出}// 如果已过期,返回 "已过期" 并标记为需要重新预约if (remainingTime < 0) {return { text: '已过期', isExpired: true };}const hours = Math.floor(remainingTime / (1000 * 60 * 60));const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60));const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);// 分时秒的格式化return { text: `${hours}小时 ${minutes}分钟 ${seconds}秒`, isExpired: false };
};
以上的函数需要注意的点如下:
由于时间的精确性和计算方式,remainingTime 很少会正好等于 0。即使时间点非常接近,由于毫秒级的差异,可能导致 remainingTime 不完全等于 0