最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3的打卡計(jì)時(shí)器實(shí)現(xiàn)方案

 更新時(shí)間:2026年03月26日 09:07:12   作者:January1207  
在日常工作和生活中,我們經(jīng)常需要記錄時(shí)間間隔,比如工作打卡、學(xué)習(xí)時(shí)長(zhǎng)、項(xiàng)目耗時(shí)等,今天我將為大家介紹一個(gè)基于Vue3的打卡計(jì)時(shí)器實(shí)現(xiàn)方案,需要的朋友可以參考下

引言

在日常工作和生活中,我們經(jīng)常需要記錄時(shí)間間隔,比如工作打卡、學(xué)習(xí)時(shí)長(zhǎng)、項(xiàng)目耗時(shí)等。今天我將為大家介紹一個(gè)基于Vue3的打卡計(jì)時(shí)器實(shí)現(xiàn)方案,它具有以下特點(diǎn):

  • ? 從打卡開(kāi)始自動(dòng)計(jì)時(shí)
  • ? 支持暫停/繼續(xù)功能
  • ? 可記錄多個(gè)時(shí)間點(diǎn)(不重置計(jì)時(shí))
  • ? 清晰的視覺(jué)反饋
  • ? 響應(yīng)式設(shè)計(jì),適配各種設(shè)備

功能概述

本計(jì)時(shí)器主要實(shí)現(xiàn)以下核心功能:

  1. 持續(xù)計(jì)時(shí):從開(kāi)始打卡起持續(xù)計(jì)時(shí),顯示當(dāng)前已用時(shí)間
  2. 暫停/繼續(xù):可隨時(shí)暫停計(jì)時(shí),再次點(diǎn)擊繼續(xù)
  3. 記錄時(shí)間點(diǎn):在計(jì)時(shí)過(guò)程中記錄關(guān)鍵時(shí)間點(diǎn),不重置計(jì)時(shí)器
  4. 重置功能:一鍵重置所有狀態(tài)和記錄
  5. 時(shí)間顯示:以時(shí):分:秒格式顯示時(shí)間

代碼實(shí)現(xiàn)

1. 完整HTML文件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>打卡計(jì)時(shí)器</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    body {
      background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    }
    .container {
      width: 100%;
      max-width: 600px;
      background: rgba(255, 255, 255, 0.95);
      border-radius: 20px;
      box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
      overflow: hidden;
    }
    .header {
      background: #2c3e50;
      color: white;
      padding: 25px;
      text-align: center;
    }
    .header h1 {
      font-size: 28px;
      margin-bottom: 10px;
    }
    .header p {
      opacity: 0.8;
      font-size: 16px;
    }
    .content {
      padding: 30px;
    }
    .timer-display {
      background: #f8f9fa;
      border-radius: 15px;
      padding: 25px;
      text-align: center;
      margin-bottom: 30px;
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    }
    .time {
      font-size: 48px;
      font-weight: bold;
      color: #2c3e50;
      margin: 10px 0;
      font-family: 'Courier New', monospace;
      letter-spacing: 2px;
    }
    .status {
      font-size: 16px;
      color: #7f8c8d;
      margin-top: 5px;
    }
    .controls {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 15px;
      margin-bottom: 30px;
    }
    .btn {
      padding: 15px;
      border: none;
      border-radius: 12px;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.3s ease;
      color: white;
    }
    .btn:hover {
      transform: translateY(-3px);
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    }
    .btn:active {
      transform: translateY(0);
    }
    .btn-start {
      background: #27ae60;
    }
    .btn-pause {
      background: #e67e22;
    }
    .btn-record {
      background: #3498db;
    }
    .btn-reset {
      background: #e74c3c;
    }
    .btn:disabled {
      background: #bdc3c7;
      cursor: not-allowed;
      transform: none;
    }
    .records {
      background: #f8f9fa;
      border-radius: 15px;
      padding: 20px;
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    }
    .records h2 {
      color: #2c3e50;
      margin-bottom: 15px;
      font-size: 22px;
      display: flex;
      align-items: center;
    }
    .records h2 i {
      margin-right: 10px;
    }
    .record-list {
      max-height: 200px;
      overflow-y: auto;
    }
    .record-item {
      display: flex;
      justify-content: space-between;
      padding: 12px 15px;
      border-bottom: 1px solid #eee;
      animation: fadeIn 0.3s ease;
    }
    .record-item:last-child {
      border-bottom: none;
    }
    .record-time {
      font-weight: 600;
      color: #3498db;
    }
    .record-duration {
      color: #7f8c8d;
      font-size: 14px;
    }
    .empty-records {
      text-align: center;
      padding: 20px;
      color: #7f8c8d;
      font-style: italic;
    }
    .footer {
      text-align: center;
      padding: 20px;
      color: #7f8c8d;
      font-size: 14px;
      border-top: 1px solid #eee;
    }
    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: translateY(0); }
    }
    @media (max-width: 500px) {
      .controls {
        grid-template-columns: 1fr;
      }
      .time {
        font-size: 36px;
      }
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <div class="header">
        <h1>打卡計(jì)時(shí)器</h1>
        <p>記錄您從打卡開(kāi)始到結(jié)束的時(shí)間</p>
      </div>
      <div class="content">
        <div class="timer-display">
          <div>計(jì)時(shí)器</div>
          <div class="time">{{ formattedTime }}</div>
          <div class="status">{{ timerStatus }}</div>
        </div>
        <div class="controls">
          <button class="btn btn-start" @click="startTimer" :disabled="isRunning">
            開(kāi)始計(jì)時(shí)
          </button>
          <button class="btn btn-pause" @click="pauseTimer" :disabled="!isRunning">
            暫停計(jì)時(shí)
          </button>
          <button class="btn btn-record" @click="recordTime" :disabled="!isRunning && elapsed === 0">
            記錄時(shí)間
          </button>
          <button class="btn btn-reset" @click="resetTimer" class="grid-column: 1 / -1;">
            重置計(jì)時(shí)器
          </button>
        </div>
        <div class="records">
          <h2>?? 時(shí)間記錄</h2>
          <div class="record-list">
            <div v-if="records.length === 0" class="empty-records">
              暫無(wú)記錄,開(kāi)始計(jì)時(shí)后點(diǎn)擊"記錄時(shí)間"按鈕
            </div>
            <div v-for="(record, index) in records" :key="index" class="record-item">
              <span class="record-time">{{ record.time }}</span>
              <span class="record-duration">持續(xù): {{ formatDuration(record.elapsed) }}</span>
            </div>
          </div>
        </div>
      </div>
      <div class="footer">
        <p>提示:開(kāi)始計(jì)時(shí)后,您可以多次記錄時(shí)間點(diǎn),計(jì)時(shí)器不會(huì)重置</p>
      </div>
    </div>
  </div>
  <script>
    const { createApp, ref, computed, onUnmounted } = Vue
    createApp({
      setup() {
        // 響應(yīng)式狀態(tài)
        const isRunning = ref(false)
        const elapsed = ref(0) // 已經(jīng)過(guò)去的時(shí)間(毫秒)
        const startTime = ref(null)
        const timer = ref(null)
        const records = ref([])
        // 計(jì)時(shí)器狀態(tài)文本
        const timerStatus = computed(() => {
          if (isRunning.value) return '計(jì)時(shí)中...'
          if (elapsed.value > 0) return '已暫停'
          return '準(zhǔn)備就緒'
        })
        // 格式化時(shí)間顯示(時(shí):分:秒)
        const formattedTime = computed(() => {
          return formatDuration(elapsed.value)
        })
        // 格式化持續(xù)時(shí)間
        function formatDuration(ms) {
          const totalSeconds = Math.floor(ms / 1000)
          const hours = Math.floor(totalSeconds / 3600)
          const minutes = Math.floor((totalSeconds % 3600) / 60)
          const seconds = totalSeconds % 60
          return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
        }
        // 開(kāi)始計(jì)時(shí)
        function startTimer() {
          if (isRunning.value) return
          isRunning.value = true
          startTime.value = Date.now() - elapsed.value
          timer.value = setInterval(() => {
            elapsed.value = Date.now() - startTime.value
          }, 100) // 每100毫秒更新一次,提高流暢度
        }
        // 暫停計(jì)時(shí)
        function pauseTimer() {
          if (!isRunning.value) return
          isRunning.value = false
          clearInterval(timer.value)
          timer.value = null
        }
        // 記錄當(dāng)前時(shí)間
        function recordTime() {
          if (elapsed.value === 0) return
          const now = new Date()
          const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
          records.value.push({
            time: timeString,
            elapsed: elapsed.value
          })
        }
        // 重置計(jì)時(shí)器
        function resetTimer() {
          pauseTimer()
          elapsed.value = 0
          records.value = []
          startTime.value = null
        }
        // 組件卸載時(shí)清理定時(shí)器
        onUnmounted(() => {
          if (timer.value) {
            clearInterval(timer.value)
          }
        })
        return {
          isRunning,
          elapsed,
          records,
          timerStatus,
          formattedTime,
          startTimer,
          pauseTimer,
          recordTime,
          resetTimer,
          formatDuration
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

2. 核心狀態(tài)管理

// 響應(yīng)式狀態(tài)
const isRunning = ref(false)          // 計(jì)時(shí)器是否正在運(yùn)行
const elapsed = ref(0)                // 已經(jīng)過(guò)去的時(shí)間(毫秒)
const startTime = ref(null)           // 計(jì)時(shí)開(kāi)始時(shí)間戳
const timer = ref(null)               // 定時(shí)器引用
const records = ref([])               // 記錄的時(shí)間點(diǎn)列表

3. 核心功能函數(shù)

開(kāi)始計(jì)時(shí)

function startTimer() {
  if (isRunning.value) return

  isRunning.value = true
  startTime.value = Date.now() - elapsed.value

  timer.value = setInterval(() => {
    elapsed.value = Date.now() - startTime.value
  }, 100) // 每100毫秒更新一次,提高流暢度
}

暫停計(jì)時(shí)

function pauseTimer() {
  if (!isRunning.value) return

  isRunning.value = false
  clearInterval(timer.value)
  timer.value = null
}

記錄時(shí)間點(diǎn)

unction recordTime() {
  if (elapsed.value === 0) return

  const now = new Date()
  const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`

  records.value.push({
    time: timeString,
    elapsed: elapsed.value
  })
}

4. 時(shí)間格式化函數(shù)

function formatDuration(ms) {
  const totalSeconds = Math.floor(ms / 1000)
  const hours = Math.floor(totalSeconds / 3600)
  const minutes = Math.floor((totalSeconds % 3600) / 60)
  const seconds = totalSeconds % 60

  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}

功能演示

使用流程

  1. 1.開(kāi)始打卡:點(diǎn)擊"開(kāi)始計(jì)時(shí)"按鈕啟動(dòng)計(jì)時(shí)器
  2. 2.持續(xù)計(jì)時(shí):計(jì)時(shí)器自動(dòng)計(jì)算并顯示從開(kāi)始到現(xiàn)在的時(shí)間
  3. 3.記錄關(guān)鍵時(shí)間點(diǎn):點(diǎn)擊"記錄時(shí)間"按鈕保存當(dāng)前時(shí)間(計(jì)時(shí)器繼續(xù)運(yùn)行)
  4. 4.暫停/繼續(xù):可以隨時(shí)暫停計(jì)時(shí),再次點(diǎn)擊繼續(xù)
  5. 5.重置:點(diǎn)擊"重置計(jì)時(shí)器"清除所有記錄和計(jì)時(shí)狀態(tài)

界面預(yù)覽

使用場(chǎng)景

這個(gè)計(jì)時(shí)器適用于多種場(chǎng)景:

  1. 1.工作打卡:記錄上班、下班時(shí)間,計(jì)算工作時(shí)長(zhǎng)
  2. 2.學(xué)習(xí)計(jì)時(shí):記錄學(xué)習(xí)開(kāi)始和結(jié)束時(shí)間,統(tǒng)計(jì)學(xué)習(xí)時(shí)長(zhǎng)
  3. 3.項(xiàng)目管理:記錄任務(wù)開(kāi)始時(shí)間,監(jiān)控項(xiàng)目進(jìn)度
  4. 4.運(yùn)動(dòng)健身:記錄運(yùn)動(dòng)開(kāi)始時(shí)間,監(jiān)控運(yùn)動(dòng)時(shí)長(zhǎng)
  5. 5.會(huì)議記錄:記錄會(huì)議開(kāi)始和關(guān)鍵節(jié)點(diǎn)時(shí)間

優(yōu)化建議

1. 本地存儲(chǔ)支持

可以添加localStorage支持,刷新頁(yè)面后保留計(jì)時(shí)狀態(tài):

// 保存到本地存儲(chǔ)
function saveToStorage() {
  localStorage.setItem('timerState', JSON.stringify({
    elapsed: elapsed.value,
    records: records.value,
    isRunning: isRunning.value
  }))
}

// 從本地存儲(chǔ)加載
function loadFromStorage() {
  const saved = localStorage.getItem('timerState')
  if (saved) {
    const state = JSON.parse(saved)
    elapsed.value = state.elapsed || 0
    records.value = state.records || []
    isRunning.value = false // 默認(rèn)不自動(dòng)運(yùn)行
  }
}

2. 導(dǎo)出功能

添加導(dǎo)出記錄功能:

unction exportRecords() {
  const data = records.value.map(r =>
    `時(shí)間點(diǎn): ${r.time}, 持續(xù)時(shí)間: ${formatDuration(r.elapsed)}`
  ).join('\n')

  const blob = new Blob([data], { type: 'text/plain' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = '計(jì)時(shí)記錄.txt'
  a.click()
  URL.revokeObjectURL(url)
}

3. 響應(yīng)式優(yōu)化

對(duì)于移動(dòng)端,可以添加觸摸事件支持:

// 在移動(dòng)端添加觸摸反饋
function addTouchFeedback() {
  const buttons = document.querySelectorAll('.btn')
  buttons.forEach(btn => {
    btn.addEventListener('touchstart', function() {
      this.style.transform = 'scale(0.95)'
    })
    btn.addEventListener('touchend', function() {
      this.style.transform = ''
    })
  })
}

總結(jié)

本文介紹了基于Vue3的打卡計(jì)時(shí)器完整實(shí)現(xiàn),涵蓋了:

  • ? 核心功能實(shí)現(xiàn)(計(jì)時(shí)、暫停、記錄、重置)
  • ? 代碼結(jié)構(gòu)和關(guān)鍵函數(shù)解析
  • ? 使用流程和界面預(yù)覽
  • ? 適用場(chǎng)景分析
  • ? 優(yōu)化建議(本地存儲(chǔ)、導(dǎo)出功能、響應(yīng)式優(yōu)化)

這個(gè)計(jì)時(shí)器組件可以直接集成到任何Vue3項(xiàng)目中,也可以作為獨(dú)立頁(yè)面使用。代碼簡(jiǎn)潔明了,易于理解和擴(kuò)展。

希望這個(gè)計(jì)時(shí)器能幫助您更好地管理時(shí)間,提高工作效率!如果您有任何問(wèn)題或建議,歡迎在評(píng)論區(qū)留言交流。

以上就是基于Vue3的打卡計(jì)時(shí)器實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于Vue3打卡計(jì)時(shí)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue實(shí)現(xiàn)一個(gè)滾動(dòng)條樣式

    vue實(shí)現(xiàn)一個(gè)滾動(dòng)條樣式

    滾動(dòng)條能夠給用戶帶來(lái)極好的體驗(yàn)效果,今天通過(guò)本文給大家分享vue實(shí)現(xiàn)一個(gè)滾動(dòng)條樣式,代碼簡(jiǎn)單易懂,需要的朋友參考下吧
    2021-07-07
  • vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則

    vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則

    這篇文章主要給大家介紹了關(guān)于vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則的相關(guān)資料,文中通過(guò)代碼介紹的介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Vue綁定對(duì)象與數(shù)組變量更改后無(wú)法渲染問(wèn)題解決

    Vue綁定對(duì)象與數(shù)組變量更改后無(wú)法渲染問(wèn)題解決

    這篇文章主要介紹了Vue綁定對(duì)象與數(shù)組變量更改后無(wú)法渲染問(wèn)題解決,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • vue3中如何使用vue-types

    vue3中如何使用vue-types

    vue-types 在 Vue 3 中的使用主要適用于希望進(jìn)行更細(xì)致的 prop 驗(yàn)證的場(chǎng)景,尤其是在 JavaScript 項(xiàng)目中,這篇文章給大家介紹vue3中如何使用vue-types,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • vue函數(shù)防抖與節(jié)流的正確使用方法

    vue函數(shù)防抖與節(jié)流的正確使用方法

    防抖和節(jié)流的作用都是防止函數(shù)多次調(diào)用,下面這篇文章主要給大家介紹了關(guān)于vue函數(shù)防抖與節(jié)流的正確使用方法,需要的朋友可以參考下
    2021-05-05
  • Vue腳手架學(xué)習(xí)之項(xiàng)目創(chuàng)建方式

    Vue腳手架學(xué)習(xí)之項(xiàng)目創(chuàng)建方式

    這篇文章主要給大家介紹了關(guān)于Vue腳手架學(xué)習(xí)之項(xiàng)目創(chuàng)建方式的相關(guān)資料,文中通過(guò)介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • vue2.x中monaco-editor的使用說(shuō)明

    vue2.x中monaco-editor的使用說(shuō)明

    這篇文章主要介紹了vue2.x中monaco-editor的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue可拖拽的瀑布流布局組件實(shí)現(xiàn)詳解

    vue可拖拽的瀑布流布局組件實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了vue的可拖拽的瀑布流布局組件有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • vue3使用xgPalyer實(shí)現(xiàn)截圖功能的方法詳解

    vue3使用xgPalyer實(shí)現(xiàn)截圖功能的方法詳解

    這篇文章主要為大家詳細(xì)介紹了如何在vue3中使用xgPalyer截圖功能,以及自定義插件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-02-02
  • uni-app 使用編輯器創(chuàng)建vue3 項(xiàng)目并且運(yùn)行的操作方法

    uni-app 使用編輯器創(chuàng)建vue3 項(xiàng)目并且運(yùn)行的操作方法

    這篇文章主要介紹了uni-app 使用編輯器創(chuàng)建vue3 項(xiàng)目并且運(yùn)行的操作方法,目前uniapp 創(chuàng)建的vue3支持 vue3.0 -- 3.2版本 也就是說(shuō)setup語(yǔ)法糖也是支持的,需要的朋友可以參考下
    2023-01-01

最新評(píng)論

沙田区| 云霄县| 邢台市| 临沂市| 吴堡县| 福建省| 涡阳县| 绍兴市| 康平县| 南陵县| 桦甸市| 荃湾区| 巴青县| 和硕县| 教育| 阳城县| 宽城| 无极县| 林芝县| 运城市| 吉隆县| 类乌齐县| 广河县| 郁南县| 凤山县| 盖州市| 扎囊县| 南乐县| 托克逊县| 汕头市| 津南区| 增城市| 闻喜县| 利川市| 山东省| 革吉县| 抚州市| 绥德县| 沂南县| 富顺县| 崇信县|