本内容使用Vue3,以及element-plus辅助开发。
首先展示倒计时器的功能:
- 手动设置倒计时器的倒计时时间
- 开始倒计时按钮
- 暂停倒计时按钮
- 重新开始倒计时按钮
其次展示倒计时任务管理界面功能:
- 创建倒计时任务
- 选择任务并进行倒计时
- 删除任务
目录
一.倒计时器:
1.html:
2.script:
(1)状态变量:
(2)格式化显示时间:
(3)初始化倒计时:
(4)开始按钮startCountdown -> 启动倒计时器:
(5)暂停按钮pauseCountdown -> 暂停倒计时器:
(6)重启倒计时器restartCountdown -> 重新开始倒计时:
(7)确定按钮 -> 修改倒计时时间:
(8)页面卸载时清理计时器(onUnMounted):
3.css:
二.倒计时任务管理界面:
1.html:
2.script:
三.倒计时器完整代码展示:
一.倒计时器:
最后面会附有完整代码,可直接CV。
1.html:
使用element-plus下拉框组件选择设置的时分秒的时间,点击确定按钮就可以将设置的时间在formattedTime显示,随后使用操作按钮进行对计时器的操作。
<el-card>
<h2>倒计时器</h2>
<!-- 倒计时时间选择框 -->
<div>
<el-form inline>
<!-- 小时选择器 -->
<el-form-item label="小时:">
<el-select v-model="selectedHours" :disabled="isRunning" placeholder="选择小时" style="width: 100px;">
<el-option v-for="h in 24" :key="'h' + h" :label="h - 1" :value="h - 1"/>
</el-select>
</el-form-item>
<!-- 分钟选择器 -->
<el-form-item label="分钟:">
<el-select v-model="selectedMinutes" :disabled="isRunning" placeholder="选择分钟" style="width: 100px;">
<el-option v-for="m in 60" :key="'m' + m" :label="m - 1" :value="m - 1"/>
</el-select>
</el-form-item>
<!-- 秒选择器 -->
<el-form-item label="秒:">
<el-select v-model="selectedSeconds" :disabled="isRunning" placeholder="选择秒" style="width: 100px;">
<el-option v-for="s in 60" :key="'s' + s" :label="s - 1" :value="s - 1"/>
</el-select>
</el-form-item>
</el-form>
<el-button type="info" @click="getTrue()">确定</el-button>
</div>
<!-- 倒计时显示 -->
<div class="timer-display">
<h1>{{ formattedTime }}</h1>
</div>
<!-- 操作按钮 -->
<div class="controls">
<!-- 倒计时器 -->
<el-button type="success" @click="startCountdown()" :disabled="isRunning">开始</el-button>
<el-button type="warning" @click="pauseCountdown()" :disabled="!isRunning">暂停</el-button>
<el-button type="primary" @click="restartCountdown()">重新开始</el-button>
<el-button type="info" @click="nextProcess()">下一阶段</el-button>
</div>
</el-card>
2.script:
(1)状态变量:
// 选择的倒计时初始值
const selectedHours = ref(0);
const selectedMinutes = ref(0);
const selectedSeconds = ref(0);
// 当前剩余的倒计时时间,以秒为单位动态更新
const totalSeconds = ref(0);
// 表示倒计时是否正在运行,用来防止重复启动计时器
const isRunning = ref(false);
// 存储定时器的句柄,用于管理 setInterval
let timer = null;
(2)格式化显示时间:
// computed:这是一个计算属性,动态计算剩余时间的格式化显示。
const formattedTime = computed(() => {
const hours = Math.floor(totalSeconds.value / 3600);
const minutes = Math.floor((totalSeconds.value % 3600) / 60);
const seconds = totalSeconds.value % 60;
// 返回格式用于倒计时器的显示 hh:mm:ss
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
});
格式化逻辑:
- 小时:
Math.floor(totalSeconds.value / 3600)
,将总秒数除以3600得到小时数(向下取整)。- 分钟:取余后计算分钟数:
Math.floor((totalSeconds.value % 3600) / 60)
。- 秒数:取余后直接取
totalSeconds.value % 60
。- 格式化:使用
padStart(2, '0')
保证个位数前补0
,显示为两位数。
(3)初始化倒计时:
// 将计算结果赋值给 totalSeconds,作为倒计时的起始值。
const initializeCountdown = () => {
totalSeconds.value = selectedHours.value * 3600 + selectedMinutes.value * 60 + selectedSeconds.value;
};
(4)开始按钮startCountdown -> 启动倒计时器:
const startCountdown = () => {
if (totalSeconds.value === 0) {
initializeCountdown(); // 如果总秒数为0,重新初始化
}
// 通过 isRunning 确保倒计时只能启动一次
if (!isRunning.value) {
isRunning.value = true;
// 使用 setInterval 每秒执行一次回调
timer = setInterval(() => {
if (totalSeconds.value > 0) {
totalSeconds.value -= 1; // 每秒减1
} else {
// 调用 pauseCountdown 停止计时器
pauseCountdown();
}
}, 1000);
}
};
(5)暂停按钮pauseCountdown -> 暂停倒计时器:
const pauseCountdown = () => {
// 将 isRunning 设置为 false,标记倒计时暂停
isRunning.value = false;
// 如果计时器已存在(timer 不为 null),调用 clearInterval 停止计时器,并将 timer 置空
if (timer) {
clearInterval(timer);
timer = null;
}
};
(6)重启倒计时器restartCountdown -> 重新开始倒计时:
const restartCountdown = () => {
// 停止当前计时器
pauseCountdown();
// 重置 totalSeconds 为初始值
initializeCountdown();
// 重新启动倒计时
startCountdown();
};
(7)确定按钮 -> 修改倒计时时间:
// 停止当前计时器,重新计算并设置倒计时总秒数
const getTrue = () => {
pauseCountdown();
initializeCountdown();
};
(8)页面卸载时清理计时器(onUnMounted):
// 当组件卸载时触发
onUnmounted(() => {
// 清理计时器,防止内存泄漏
if (timer) {
clearInterval(timer);
}
});
3.css:
<style lang="css" scoped>
.countdown-timer {
text-align: center;
font-family: Arial, sans-serif;
}
.timer-display h1 {
font-size: 48px;
margin: 20px 0;
}
.controls button {
margin: 5px;
padding: 10px 20px;
font-size: 16px;
}
select {
margin: 5px;
padding: 5px;
}
</style>
二.倒计时任务管理界面:
通过弹窗填写任务名称和时间(时、分、秒),并将任务提交到任务列表中。
1.html:
<el-card>
<!-- 倒计时时间选择框 -->
<el-button type="primary" @click="addTaskDialog()">创建任务</el-button>
<!-- 任务队列 -->
<h3>任务队列</h3>
<el-table :data="timeTaskData" style="width: 100%" height="250">
<el-table-column prop="name" label="任务名称" width="120" />
<el-table-column prop="time" label="倒计时间" width="120" />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button type="success" @click="setCountdownTime(row)" :icon="Check" circle />
<el-button type="danger" @click="deleteTimeTask(row)" :icon="Delete" circle />
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 任务创建弹窗 -->
<el-dialog v-model="taskDialogVisible" title="创建新任务" width="500" draggable>
<el-form>
<el-form-item label="任务名称">
<el-input v-model="newTask.name" placeholder="请输入任务名称" />
</el-form-item>
<el-form-item label="小时">
<el-select v-model="newTask.hours" placeholder="选择小时">
<el-option :key="'h0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="h in 24" :key="'h' + h" :label="h" :value="h" />
</el-select>
</el-form-item>
<el-form-item label="分钟">
<el-select v-model="newTask.minutes" placeholder="选择分钟">
<el-option :key="'m0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="m in 60" :key="'m' + m" :label="m" :value="m" />
</el-select>
</el-form-item>
<el-form-item label="秒">
<el-select v-model="newTask.seconds" placeholder="选择秒">
<el-option :key="'s0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="s in 60" :key="'s' + s" :label="s" :value="s" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="taskDialogVisible = false">取消</el-button>
<el-button type="primary" @click="addTimeTask()">创建任务</el-button>
</div>
</template>
</el-dialog>
2.script:
// 用于控制弹窗的显示和隐藏状态
const taskDialogVisible = ref(false);
// 打开弹窗
const addTaskDialog = () =>{
taskDialogVisible.value = true;
console.log('弹窗状态:', taskDialogVisible.value); // 调试日志
}
// 用于绑定用户输入的任务数据
const newTask = reactive({
name: '', // 任务名称
// 用户输入的时、分、秒,表示任务的时间
hours: '',
minutes: '',
seconds: ''
})
// 任务列表
const timeTaskData = ref([]);
const timeTask = reactive({
name: "",
time: null, // 任务时间,格式为 HH:mm:ss
roomGroup : ""
});
// 清空 timeTask 中的任务数据
const cleantimeRask = () =>{
timeTask.value = {
name: "",
time: null,
roomGroup : ""
};
}
const addTimeTask = async () =>{
taskDialogVisible.value = false;
// 使用用户输入的时、分、秒来设置时间
const hours = parseInt(newTask.hours) || 0;
const minutes = parseInt(newTask.minutes) || 0;
const seconds = parseInt(newTask.seconds) || 0;
// 将时间合并为 HH:mm:ss 格式
const timeString = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
// 设置任务数据
timeTask.time = timeString; // 使用标准的 HH:mm:ss 格式
// 从 userStore 获取当前用户的分组
timeTask.roomGroup = userStore.user.roomGroup;
timeTask.name = newTask.name;
if (timeTask.name !== '' && timeTask.time !== '') {
await insertTimeTask(timeTask); // 插入任务(异步操作)
ElMessage.success("任务添加成功");
getTimeTaskByRGToShow(); // 更新任务列表
console.log(timeTaskData.value);
cleantimeRask(); // 清空当前任务数据
} else {
ElMessage.error("任务名称或时间不能为空");
}
}
// 设置倒计时的时、分、秒
const setCountdownTime = (row) => {
let date;
if (row.time instanceof Date) {
date = row.time; // 如果是 Date 对象,直接赋值
} else if (typeof row.time === "string") {
const [hours, minutes, seconds] = row.time.split(":").map(Number); // 按 HH:mm:ss 分割
date = new Date(); // 使用当前日期
date.setHours(hours, minutes, seconds); // 设置时间
} else {
console.error("无效的时间格式");
return;
}
selectedHours.value = date.getHours(); // 获取小时
selectedMinutes.value = date.getMinutes(); // 获取分钟
selectedSeconds.value = date.getSeconds(); // 获取秒数
initializeCountdown(); // 更新总时间
};
// 删除倒计时的任务
const deleteTimeTask = async (row) =>{
await deleteTimeTaskById(row.id);
ElMessage.success("任务删除成功");
getTimeTaskByRGToShow();
console.log(timeTaskData.value);
};
// 根据组别获取所有任务信息
const getTimeTaskByRGToShow = async () =>{
let roomGroup = userStore.user.roomGroup;
let result = await getTimeTaskByRG(roomGroup);
timeTaskData.value = result.data;
}
getTimeTaskByRGToShow();
三.倒计时器完整代码展示:
1.html:
<el-main style="background-color:darkgray">
<div>
<el-card>
<!-- 倒计时时间选择框 -->
<el-button type="primary" @click="addTaskDialog()">创建任务</el-button>
<!-- 任务队列 -->
<h3>任务队列</h3>
<el-table :data="timeTaskData" style="width: 100%" height="250">
<el-table-column prop="name" label="任务名称" width="120" />
<el-table-column prop="time" label="倒计时间" width="120" />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button type="success" @click="setCountdownTime(row)" :icon="Check" circle />
<el-button type="danger" @click="deleteTimeTask(row)" :icon="Delete" circle />
<!-- <el-button type="success" >开始倒计时</el-button> -->
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<div class="countdown-timer">
<el-card>
<h2>倒计时器</h2>
<!-- 倒计时时间选择框 -->
<div>
<el-form inline>
<!-- 小时选择器 -->
<el-form-item label="小时:">
<el-select v-model="selectedHours" :disabled="isRunning" placeholder="选择小时" style="width: 100px;">
<el-option v-for="h in 24" :key="'h' + h" :label="h - 1" :value="h - 1"/>
</el-select>
</el-form-item>
<!-- 分钟选择器 -->
<el-form-item label="分钟:">
<el-select v-model="selectedMinutes" :disabled="isRunning" placeholder="选择分钟" style="width: 100px;">
<el-option v-for="m in 60" :key="'m' + m" :label="m - 1" :value="m - 1"/>
</el-select>
</el-form-item>
<!-- 秒选择器 -->
<el-form-item label="秒:">
<el-select v-model="selectedSeconds" :disabled="isRunning" placeholder="选择秒" style="width: 100px;">
<el-option v-for="s in 60" :key="'s' + s" :label="s - 1" :value="s - 1"/>
</el-select>
</el-form-item>
</el-form>
<el-button type="info" @click="getTrue()">确定</el-button>
</div>
<!-- 倒计时显示 -->
<div class="timer-display">
<h1>{{ formattedTime }}</h1>
</div>
<!-- 操作按钮 -->
<div class="controls">
<!-- 倒计时器 -->
<el-button type="success" @click="startCountdown()" :disabled="isRunning">开始</el-button>
<el-button type="warning" @click="pauseCountdown()" :disabled="!isRunning">暂停</el-button>
<el-button type="primary" @click="restartCountdown()">重新开始</el-button>
<el-button type="info" @click="nextProcess()">下一阶段</el-button>
</div>
</el-card>
</div>
</el-main>
<!-- 任务创建弹窗 -->
<el-dialog v-model="taskDialogVisible" title="创建新任务" width="500" draggable>
<el-form>
<el-form-item label="任务名称">
<el-input v-model="newTask.name" placeholder="请输入任务名称" />
</el-form-item>
<el-form-item label="小时">
<el-select v-model="newTask.hours" placeholder="选择小时">
<el-option :key="'h0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="h in 24" :key="'h' + h" :label="h" :value="h" />
</el-select>
</el-form-item>
<el-form-item label="分钟">
<el-select v-model="newTask.minutes" placeholder="选择分钟">
<el-option :key="'m0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="m in 60" :key="'m' + m" :label="m" :value="m" />
</el-select>
</el-form-item>
<el-form-item label="秒">
<el-select v-model="newTask.seconds" placeholder="选择秒">
<el-option :key="'s0'" :label="0" :value="0" /> <!-- 手动添加 0 -->
<el-option v-for="s in 60" :key="'s' + s" :label="s" :value="s" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="taskDialogVisible = false">取消</el-button>
<el-button type="primary" @click="addTimeTask()">创建任务</el-button>
</div>
</template>
</el-dialog>
2.script:
<script setup>
import { reactive, ref, computed, onUnmounted } from 'vue';
import { Check,Delete,Star } from '@element-plus/icons-vue';
import { getCpByRG, insertByRG } from '@/api/competition';
import { getTimeTaskByRG,insertTimeTask,deleteTimeTaskById } from '@/api/timetask';
import { useUserStore } from '@/stores/user';
import { ElMessage } from 'element-plus';
const userStore = useUserStore();
// 弹窗组件
const taskDialogVisible = ref(false);
const addTaskDialog = () =>{
taskDialogVisible.value = true;
console.log('弹窗状态:', taskDialogVisible.value); // 调试日志
}
const newTask = reactive({
name: '',
hours: '',
minutes: '',
seconds: ''
})
const timeTaskData = ref([]);
const timeTask = reactive({
name: "",
time: null,
roomGroup : ""
});
const cleantimeRask = () =>{
timeTask.value = {
name: "",
time: null,
roomGroup : ""
};
}
const addTimeTask = async () =>{
taskDialogVisible.value = false;
// 用用户输入的时、分、秒来设置时间
const hours = parseInt(newTask.hours) || 0;
const minutes = parseInt(newTask.minutes) || 0;
const seconds = parseInt(newTask.seconds) || 0;
// 将时间合并为 HH:mm:ss 格式
const timeString = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
// 设置任务数据
timeTask.time = timeString; // 使用标准的 HH:mm:ss 格式
timeTask.roomGroup = userStore.user.roomGroup;
timeTask.name = newTask.name;
if (timeTask.name !== '' && timeTask.time !== '') {
await insertTimeTask(timeTask);
ElMessage.success("任务添加成功");
getTimeTaskByRGToShow();
console.log(timeTaskData.value);
cleantimeRask();
} else {
ElMessage.error("任务名称或时间不能为空");
}
}
// 设置倒计时的时、分、秒
const setCountdownTime = (row) => {
let date;
if (row.time instanceof Date) {
date = row.time; // 如果是 Date 对象,直接赋值
} else if (typeof row.time === "string") {
const [hours, minutes, seconds] = row.time.split(":").map(Number); // 按 HH:mm:ss 分割
date = new Date(); // 使用当前日期
date.setHours(hours, minutes, seconds); // 设置时间
} else {
console.error("无效的时间格式");
return;
}
selectedHours.value = date.getHours(); // 获取小时
selectedMinutes.value = date.getMinutes(); // 获取分钟
selectedSeconds.value = date.getSeconds(); // 获取秒数
initializeCountdown(); // 更新总时间
};
// 删除倒计时的任务
const deleteTimeTask = async (row) =>{
await deleteTimeTaskById(row.id);
ElMessage.success("任务删除成功");
getTimeTaskByRGToShow();
console.log(timeTaskData.value);
};
// 根据组别获取所有任务信息
const getTimeTaskByRGToShow = async () =>{
let roomGroup = userStore.user.roomGroup;
let result = await getTimeTaskByRG(roomGroup);
timeTaskData.value = result.data;
}
getTimeTaskByRGToShow();
/
// 倒计时器
// 选择的倒计时初始值
const selectedHours = ref(0);
const selectedMinutes = ref(0);
const selectedSeconds = ref(0);
// 当前倒计时时间(以秒为单位)
const totalSeconds = ref(0);
const isRunning = ref(false);
let timer = null;
// 格式化显示的时间
const formattedTime = computed(() => {
const hours = Math.floor(totalSeconds.value / 3600);
const minutes = Math.floor((totalSeconds.value % 3600) / 60);
const seconds = totalSeconds.value % 60;
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
});
// 初始化倒计时
const initializeCountdown = () => {
totalSeconds.value = selectedHours.value * 3600 + selectedMinutes.value * 60 + selectedSeconds.value;
};
// 开始倒计时
const startCountdown = () => {
// 如果当前总秒数为 0,自动初始化倒计时
if (totalSeconds.value === 0) {
initializeCountdown();
}
if (!isRunning.value) {
isRunning.value = true;
playSound('start'); // 播放开始音效
timer = setInterval(() => {
if (totalSeconds.value > 0) {
totalSeconds.value -= 1;
} else {
pauseCountdown();
playSound('end'); // 倒计时结束时播放音效
}
}, 1000);
}
};
// 暂停倒计时
const pauseCountdown = () => {
isRunning.value = false;
playSound('pause'); // 播放暂停音效
if (timer) {
clearInterval(timer);
timer = null;
}
};
// 重新开始倒计时
const restartCountdown = () => {
pauseCountdown();
initializeCountdown();
playSound('reset'); // 播放重置音效
startCountdown();
};
// 确定修改倒计时时间
const getTrue = () => {
pauseCountdown();
initializeCountdown();
};
// 页面卸载时清理计时器
onUnmounted(() => {
if (timer) {
clearInterval(timer);
}
});
</script>
3.css:
<style lang="css" scoped>
.countdown-timer {
text-align: center;
font-family: Arial, sans-serif;
}
.timer-display h1 {
font-size: 48px;
margin: 20px 0;
}
.controls button {
margin: 5px;
padding: 10px 20px;
font-size: 16px;
}
select {
margin: 5px;
padding: 5px;
}
</style>