Files
web/app/components/common/CountdownTimer.vue
2026-03-02 09:45:33 +07:00

135 lines
3.0 KiB
Vue

<!-- CountdownTimer.vue -->
<template>
<div class="countdown-wrapper">
<span v-if="isExpired" class="tag is-danger">
{{ isVietnamese ? 'Hết giờ' : 'Expired' }}
</span>
<span v-else class="tag" :class="tagClass">
<span class="countdown-text">{{ formattedTime }}</span>
</span>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
import { useStore } from '@/stores/index'
const props = defineProps({
dateValue: {
type: [String, Date],
required: true
},
format: {
type: String,
default: 'HH:mm:ss'
}
})
const store = useStore()
const { $dayjs } = useNuxtApp()
const timeRemaining = ref({
days: 0,
hours: 0,
minutes: 0,
seconds: 0
})
const isExpired = ref(false)
let intervalId = null
const isVietnamese = computed(() => store.lang === 'vi')
const tagClass = computed(() => {
const totalSeconds = timeRemaining.value.days * 86400 +
timeRemaining.value.hours * 3600 +
timeRemaining.value.minutes * 60 +
timeRemaining.value.seconds
if (totalSeconds <= 0) return 'is-danger'
if (totalSeconds <= 3600) return 'is-warning' // <= 1 hour
if (totalSeconds <= 86400) return 'is-info' // <= 1 day
return 'is-primary' // > 1 day
})
const formattedTime = computed(() => {
const { days, hours, minutes, seconds } = timeRemaining.value
if (days > 0) {
return isVietnamese
? `${days}d ${hours}h ${minutes}m`
: `${days}d ${hours}h ${minutes}m`
}
if (hours > 0) {
return isVietnamese
? `${hours}h ${minutes}m ${seconds}s`
: `${hours}h ${minutes}m ${seconds}s`
}
return isVietnamese
? `${minutes}m ${seconds}s`
: `${minutes}m ${seconds}s`
})
const calculateTimeRemaining = () => {
try {
const targetDate = $dayjs(props.dateValue)
const now = $dayjs()
if (now.isAfter(targetDate)) {
isExpired.value = true
timeRemaining.value = { days: 0, hours: 0, minutes: 0, seconds: 0 }
return
}
isExpired.value = false
const diff = targetDate.diff(now, 'second')
const days = Math.floor(diff / 86400)
const hours = Math.floor((diff % 86400) / 3600)
const minutes = Math.floor((diff % 3600) / 60)
const seconds = diff % 60
timeRemaining.value = { days, hours, minutes, seconds }
} catch (error) {
console.error('Error calculating countdown:', error)
isExpired.value = true
}
}
const startCountdown = () => {
calculateTimeRemaining()
if (intervalId) clearInterval(intervalId)
intervalId = setInterval(() => {
calculateTimeRemaining()
if (isExpired.value && intervalId) {
clearInterval(intervalId)
intervalId = null
}
}, 1000)
}
watch(() => props.dateValue, () => {
startCountdown()
}, { deep: true })
onMounted(() => {
startCountdown()
})
onBeforeUnmount(() => {
if (intervalId) {
clearInterval(intervalId)
}
})
</script>
<style scoped>
.countdown-wrapper {
display: inline-block;
}
</style>