Files
web/app/components/common/CountdownTimer.vue
2026-05-05 11:06:49 +07:00

142 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>