my-timer/src/Timer.vue

<template>
  <div :class="classes">{{displayValue}}</div>
</template>

<script>

  /**
   * 计时器组件
   * @module $ui/components/my-timer
   */

  import Dayjs from 'dayjs'

  export default {
    name: 'MyTimer',
    /**
     * 属性参数
     * @member props
     * @property {string|number} [target] 设定值,String为时间格式,Number是秒数,  countdown为true, 是开始值, countdown为false时结束值
     * @property {string} [format=HH:mm:ss] 显示格式
     * @property {boolean} [countdown=false] 倒数模式
     * @property {string} [type] 颜色,可选值 'primary', 'success', 'warning', 'danger', 'info'
     * @property {boolean} [auto=false] 自动运行
     * @property {boolean} [interval=1000] 执行时间隔 单位 ms
     */
    props: {
      // 设定值,String为时间格式,Number是秒数,  countdown为true, 是开始值, countdown为false时结束值
      target: [String, Number],
      // 显示格式
      format: {
        type: String,
        default: 'HH:mm:ss'
      },
      // 倒数模式
      countdown: Boolean,
      // 颜色
      type: {
        type: String,
        validator(val) {
          return ['primary', 'success', 'warning', 'danger', 'info'].includes(val)
        }
      },
      // 自动开始
      auto: Boolean,
      // 执行时间隔 单位 ms
      interval: {
        type: Number,
        default: 1000
      }
    },
    data() {
      this.timerId = null
      return {
        dayjs: null,
        targetDayjs: null,
        minDayjs: null,
        isFinish: false
      }
    },
    computed: {
      classes() {
        return {
          'my-timer': true,
          [`is-${this.type}`]: !!this.type,
          'is-finish': this.isFinish
        }
      },
      displayValue() {
        return this.dayjs ? this.dayjs.format(this.format) : ''
      }
    },
    methods: {
      init() {
        this.reset()
        this.auto && this.start()
      },
      getTarget() {
        if (!this.target) {
          return this.getMax()
        }

        if (typeof this.target === 'number') {
          return this.getMin().second(this.target)
        }

        if (typeof this.target === 'string') {
          return Dayjs(`${this.getMin().format('YYYY-MM-DD')} ${this.target}`)
        }
      },
      getMin() {
        return Dayjs()
          .hour(0)
          .minute(0)
          .second(0)
          .millisecond(0)
      },
      getMax() {
        return Dayjs()
          .hour(23)
          .minute(59)
          .second(59)
      },
      /**
       * 开始运行
       * @method start
       */
      start() {
        if (!this.dayjs) return
        clearInterval(this.timerId)
        this.timerId = setInterval(() => {
          this.tick()
        }, this.interval)
        /**
         * 开始运行时触发
         * @event start
         * @param {Dayjs} dayjs 时间对象
         */
        this.$emit('start', this.dayjs)
      },
      /**
       * 停止运行
       * @method stop
       */
      stop() {
        clearInterval(this.timerId)
        /**
         * 停止时触发
         * @event stop
         * @param {Dayjs} dayjs 时间对象
         */
        this.$emit('stop', this.dayjs)
      },
      /**
       * 重置
       * @method reset
       */
      reset() {
        this.isFinish = false
        clearInterval(this.timerId)
        this.minDayjs = Object.freeze(this.getMin())
        this.targetDayjs = Object.freeze(this.getTarget())
        this.dayjs = this.countdown
          ? Object.freeze(this.getTarget())
          : Object.freeze(this.getMin())
        /**
         * 重置时触发
         * @event reset
         * @param {Dayjs} dayjs 时间对象
         */
        this.$emit('reset', this.dayjs)
      },
      tick() {
        if (!this.dayjs) return
        if (this.countdown) {
          this.dayjs = Object.freeze(this.dayjs.subtract(this.interval, 'millisecond'))
          if (this.dayjs.valueOf() === this.minDayjs.valueOf()) {
            this.isFinish = true
          }
        } else {
          this.dayjs = Object.freeze(this.dayjs.add(this.interval, 'millisecond'))
          if (this.dayjs.valueOf() === this.targetDayjs.valueOf()) {
            this.isFinish = true
          }
        }
        if (this.isFinish) {
          clearInterval(this.timerId)
          /**
           * 完成时触发
           * @event finish
           * @param {Dayjs} dayjs 时间对象
           */
          this.$emit('finish', this.dayjs)
        } else {
          /**
           * 时间跳动时触发
           * @event tick
           * @param {Dayjs} dayjs 时间对象
           */
          this.$emit('tick', this.dayjs)
        }
      }
    },
    watch: {
      target() {
        this.init()
      }
    },
    mounted() {
      this.init()
    },
    beforeDestroy() {
      clearInterval(this.timerId)
    }
  }
</script>