|
@@ -0,0 +1,146 @@
|
|
|
+<!-- src/App.vue -->
|
|
|
+<template>
|
|
|
+ <div class="timer-app">
|
|
|
+ <h1>倒计时计时器</h1>
|
|
|
+
|
|
|
+ <div class="input-section">
|
|
|
+ <div>
|
|
|
+ <label for="hours">小时:</label>
|
|
|
+ <input type="number" id="hours" v-model.number="hours" min="0">
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label for="minutes">分钟:</label>
|
|
|
+ <input type="number" id="minutes" v-model.number="minutes" min="0" max="59">
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label for="seconds">秒:</label>
|
|
|
+ <input type="number" id="seconds" v-model.number="seconds" min="0" max="59">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="time-display">{{ formattedTime }}</div>
|
|
|
+
|
|
|
+ <div class="controls">
|
|
|
+ <button @click="startTimer" :disabled="isRunning">{{ startButtonText }}</button>
|
|
|
+ <button @click="resetTimer" :disabled="!hasStarted">重置</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="showHistory" class="history">
|
|
|
+ <h3>最近使用:</h3>
|
|
|
+ <ul>
|
|
|
+ <li v-for="time in recentTimes" @click="selectRecentTime(time)">{{ time }}</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onUnmounted } from 'vue'
|
|
|
+
|
|
|
+// 响应式数据
|
|
|
+const hours = ref(0)
|
|
|
+const minutes = ref(0)
|
|
|
+const seconds = ref(0)
|
|
|
+const totalSeconds = ref(0)
|
|
|
+const remainingSeconds = ref(0)
|
|
|
+const timerId = ref(null)
|
|
|
+const isRunning = ref(false)
|
|
|
+const hasStarted = ref(false)
|
|
|
+
|
|
|
+// 计算属性
|
|
|
+const formattedTime = computed(() => {
|
|
|
+ const h = Math.floor(remainingSeconds.value / 3600)
|
|
|
+ const m = Math.floor((remainingSeconds.value % 3600) / 60)
|
|
|
+ const s = remainingSeconds.value % 60
|
|
|
+ return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
|
|
|
+})
|
|
|
+
|
|
|
+const startButtonText = computed(() => {
|
|
|
+ if (isRunning.value) return '暂停'
|
|
|
+ if (hasStarted.value) return '继续'
|
|
|
+ return '开始'
|
|
|
+})
|
|
|
+
|
|
|
+// 方法
|
|
|
+const startTimer = () => {
|
|
|
+ if (isRunning.value) {
|
|
|
+ // 暂停计时器
|
|
|
+ clearInterval(timerId.value)
|
|
|
+ isRunning.value = false
|
|
|
+ } else {
|
|
|
+ if (!hasStarted.value) {
|
|
|
+ // 首次启动,计算总秒数
|
|
|
+ totalSeconds.value = hours.value * 3600 + minutes.value * 60 + seconds.value
|
|
|
+ remainingSeconds.value = totalSeconds.value
|
|
|
+
|
|
|
+ // 保存到历史记录
|
|
|
+ saveRecentTime(`${hours.value}时${minutes.value}分${seconds.value}秒`)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 启动计时器
|
|
|
+ timerId.value = setInterval(() => {
|
|
|
+ remainingSeconds.value--
|
|
|
+ if (remainingSeconds.value <= 0) {
|
|
|
+ clearInterval(timerId.value)
|
|
|
+ isRunning.value = false
|
|
|
+ playEndSound() // 自定义函数
|
|
|
+ }
|
|
|
+ }, 1000)
|
|
|
+
|
|
|
+ isRunning.value = true
|
|
|
+ hasStarted.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const resetTimer = () => {
|
|
|
+ clearInterval(timerId.value)
|
|
|
+ hours.value = 0
|
|
|
+ minutes.value = 0
|
|
|
+ seconds.value = 0
|
|
|
+ totalSeconds.value = 0
|
|
|
+ remainingSeconds.value = 0
|
|
|
+ isRunning.value = false
|
|
|
+ hasStarted.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 生命周期钩子
|
|
|
+onUnmounted(() => {
|
|
|
+ clearInterval(timerId.value)
|
|
|
+})
|
|
|
+
|
|
|
+// 历史记录功能(需自行实现)
|
|
|
+const recentTimes = ref([])
|
|
|
+const showHistory = ref(false)
|
|
|
+
|
|
|
+const saveRecentTime = (time) => {
|
|
|
+ // 实现保存到本地存储的逻辑
|
|
|
+}
|
|
|
+
|
|
|
+const selectRecentTime = (time) => {
|
|
|
+ // 实现从历史记录加载时间的逻辑
|
|
|
+}
|
|
|
+
|
|
|
+const playEndSound = () => {
|
|
|
+ // 实现播放提示音的逻辑
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 自定义样式(建议使用 Tailwind CSS 类代替) */
|
|
|
+.timer-app {
|
|
|
+ max-width: 400px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 20px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.time-display {
|
|
|
+ font-size: 3rem;
|
|
|
+ margin: 20px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.controls button {
|
|
|
+ padding: 10px 20px;
|
|
|
+ margin: 0 5px;
|
|
|
+}
|
|
|
+</style>
|