time-clock.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. <template>
  2. <div class="time-counter">
  3. {{ formattedTime }}
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: 'TimeClock',
  9. // props 定义(与 Vue3 逻辑一致,保留非负整数校验)
  10. props: {
  11. initialSeconds: {
  12. type: Number,
  13. required: true,
  14. validator(value) {
  15. // 校验:必须是非负整数
  16. return Number.isInteger(value) && value >= 0
  17. }
  18. }
  19. },
  20. data() {
  21. return {
  22. // 基础毫秒(作为起点)
  23. baseMillis: Math.floor(this.initialSeconds),
  24. // 启动时间戳(用于计算增量)
  25. startTs: Date.now(),
  26. // 全局tick推送的当前时间(订阅更新)
  27. nowTs: Date.now()
  28. }
  29. },
  30. computed: {
  31. // 格式化时间:毫秒 → 00:00:00.00 格式(毫秒两位)
  32. formattedTime() {
  33. const elapsed = this.baseMillis + (this.nowTs - this.startTs)
  34. const totalMs = Math.max(0, Math.floor(elapsed))
  35. const hours = String(Math.floor(totalMs / 3600000)).padStart(2, '0')
  36. const minutes = String(Math.floor((totalMs % 3600000) / 60000)).padStart(2, '0')
  37. const seconds = String(Math.floor((totalMs % 60000) / 1000)).padStart(2, '0')
  38. // const ms2 = String(Math.floor((totalMs % 1000) / 10)).padStart(2, '0')
  39. return `${hours}:${minutes}:${seconds}`
  40. }
  41. },
  42. watch: {
  43. // 监听初始秒数变化(对应 Vue3 的 watch 函数)
  44. initialSeconds: {
  45. handler(newVal) {
  46. // 同步为毫秒值并重置起点
  47. const safeVal = Number(newVal)
  48. this.baseMillis = Number.isFinite(safeVal) ? Math.floor(safeVal) : 0
  49. this.startTs = Date.now()
  50. },
  51. immediate: true
  52. }
  53. },
  54. methods: {
  55. // 订阅全局tick(单例定时器,所有实例共享,降低性能开销)
  56. subscribeTick() {
  57. const scope = globalThis.__timeClockScope || (globalThis.__timeClockScope = {
  58. subs: new Set(),
  59. timer: null
  60. })
  61. const cb = () => { this.nowTs = Date.now() }
  62. scope.subs.add(cb)
  63. if (!scope.timer) {
  64. // 每秒触发一次,保持跳秒显示与两位毫秒展示
  65. scope.timer = setInterval(() => {
  66. const now = Date.now()
  67. scope.subs.forEach(fn => fn(now))
  68. }, 1000)
  69. }
  70. // 返回取消订阅函数
  71. return () => {
  72. scope.subs.delete(cb)
  73. if (scope.subs.size === 0 && scope.timer) {
  74. clearInterval(scope.timer)
  75. scope.timer = null
  76. }
  77. }
  78. }
  79. },
  80. // 初始化时启动计时(对应 Vue3 的 setup 初始化逻辑)
  81. mounted() {
  82. this._unsub = this.subscribeTick()
  83. },
  84. // 组件卸载时清理定时器(避免内存泄漏,与 Vue3 一致)
  85. beforeDestroy() {
  86. if (this._unsub) this._unsub()
  87. }
  88. }
  89. </script>
  90. <style scoped>
  91. .time-counter {
  92. font-size: 14px;
  93. font-weight: 600;
  94. color: #ff0000;
  95. letter-spacing: 2px;
  96. }
  97. </style>