web_ppt / frontend /src /views /Screen /CountdownTimer.vue
CatPtain's picture
Upload 339 files
89ce340 verified
<template>
<MoveablePanel
class="countdown-timer"
:width="180"
:height="110"
:left="left"
:top="top"
>
<div class="header">
<span class="text-btn" @click="toggle()">{{ inTiming ? '暂停' : '开始'}}</span>
<span class="text-btn" @click="reset()">重置</span>
<span class="text-btn" @click="toggleCountdown()" :class="{ 'active': isCountdown }">倒计时</span>
</div>
<div class="content">
<div class="timer">
<input
type="text"
:value="fillDigit(minute, 2)"
:maxlength="3" :disabled="inputEditable"
@mousedown.stop
@blur="$event => changeTime($event, 'minute')"
@keydown.stop
@keydown.enter.stop="$event => changeTime($event, 'minute')"
>
</div>
<div class="colon">:</div>
<div class="timer">
<input
type="text"
:value="fillDigit(second, 2)"
:maxlength="3" :disabled="inputEditable"
@mousedown.stop
@blur="$event => changeTime($event, 'second')"
@keydown.stop
@keydown.enter.stop="$event => changeTime($event, 'second')"
>
</div>
</div>
<div class="close-btn" @click="emit('close')"><IconClose class="icon" /></div>
</MoveablePanel>
</template>
<script lang="ts" setup>
import { computed, onUnmounted, ref } from 'vue'
import { fillDigit } from '@/utils/common'
import MoveablePanel from '@/components/MoveablePanel.vue'
withDefaults(defineProps<{
left?: number
top?: number
}>(), {
left: 5,
top: 5,
})
const emit = defineEmits<{
(event: 'close'): void
}>()
const timer = ref<number | null>(null)
const inTiming = ref(false)
const isCountdown = ref(false)
const time = ref(0)
const minute = computed(() => Math.floor(time.value / 60))
const second = computed(() => time.value % 60)
const inputEditable = computed(() => {
return !isCountdown.value || inTiming.value
})
const clearTimer = () => {
if (timer.value) clearInterval(timer.value)
}
onUnmounted(clearTimer)
const pause = () => {
clearTimer()
inTiming.value = false
}
const reset = () => {
clearTimer()
inTiming.value = false
if (isCountdown.value) time.value = 600
else time.value = 0
}
const start = () => {
clearTimer()
if (isCountdown.value) {
timer.value = setInterval(() => {
time.value = time.value - 1
if (time.value <= 0) reset()
}, 1000)
}
else {
timer.value = setInterval(() => {
time.value = time.value + 1
if (time.value > 36000) pause()
}, 1000)
}
inTiming.value = true
}
const toggle = () => {
if (inTiming.value) pause()
else start()
}
const toggleCountdown = () => {
isCountdown.value = !isCountdown.value
reset()
}
const changeTime = (e: FocusEvent | KeyboardEvent, type: 'minute' | 'second') => {
const inputRef = e.target as HTMLInputElement
let value = inputRef.value
const isNumber = /^(\d)+$/.test(value)
if (isNumber) {
if (type === 'second' && +value >= 60) value = '59'
time.value = type === 'minute' ? (+value * 60 + second.value) : (+value + minute.value * 60)
}
else inputRef.value = type === 'minute' ? fillDigit(minute.value, 2) : fillDigit(second.value, 2)
}
</script>
<style lang="scss" scoped>
.countdown-timer {
user-select: none;
}
.header {
height: 16px;
font-size: 13px;
margin-bottom: 16px;
display: flex;
align-items: center;
.text-btn {
margin-right: 8px;
cursor: pointer;
&:hover, &.active {
color: $themeColor;
}
}
}
.content {
display: flex;
justify-content: space-between;
padding: 0 5px;
}
.timer {
width: 54px;
height: 54px;
border-radius: 50%;
background-color: rgba($color: $themeColor, $alpha: .05);
overflow: hidden;
input {
width: 100%;
height: 100%;
border: 0;
outline: 0;
background-color: transparent;
text-align: center;
font-size: 22px;
}
}
.colon {
height: 54px;
line-height: 54px;
font-size: 22px;
}
.icon-btn {
width: 20px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.pause, .play {
font-size: 17px;
}
.reset {
font-size: 12px;
}
.close-btn {
position: absolute;
top: 0;
right: 0;
padding: 10px;
line-height: 1;
cursor: pointer;
}
</style>