<!--
  @description: 竞价大厅倒计时组件
  @Modifier: hdk
  @routerPath:
  @date: 2020/10/27
-->
<template>
  <div>
    <div v-if="msTime.show" class="countdown">
      <p v-if="msTime.day>0" class="time-num">
        <span>{{ msTime.day }}</span>
        <i class="dateText">天</i>
      </p>
      <p v-if="msTime.day>0" class="point-of-time">
        :
      </p>
      <p class="time-num">
        <span>{{ msTime.hour[0] }}</span>
        <span>{{ msTime.hour[1] }}</span>
        <i class="dateText">时</i>
      </p>
      <p class="point-of-time">
        :
      </p>
      <p class="time-num">
        <span>{{ msTime.minutes[0] }}</span>
        <span>{{ msTime.minutes[1] }}</span>
        <i class="dateText">分</i>
      </p>
      <p class="point-of-time">
        :
      </p>
      <p class="time-num">
        <span>{{ msTime.seconds[0] }}</span>
        <span>{{ msTime.seconds[1] }}</span>
        <i class="dateText">秒</i>
      </p>
    </div>
    <p v-if="!msTime.show">
      {{ endText }}
    </p>
  </div>
</template>

<script>
/* api*/
import { getTime } from '@api/common/tools.js' // 获取服务器时间
export default {
  name: 'Countdown',
  props: {
    // 当前时间
    sysCurrentTime: {
      type: String || Number,
      default: ''
    },
    // 活动结束时间
    endTime: {
      type: String,
      default: ''
    },
    // 活动开始时间
    startTime: {
      type: String,
      default: ''
    },
    // 倒计时结束显示文本
    endText: {
      type: String,
      default: '倒计时结束'
    },
    // 今日标段列表下标
    index: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      msTime: {			// 倒计时数值
        show: false,		// 倒计时状态
        day: '',			// 天
        hour: '',		// 小时
        minutes: '',		// 分钟
        seconds: ''		// 秒
      },
      end: '',	// 活动结束时间
      current: '', // 当前时间
      currentTime: '', // 记录系统时间
      winStCurrentTime: '', // 本地终端时间-定时器起始时间
      count: 0, // 标记执行次数
      interval: 1000, // 倒计时执行间隔时间 1000 = 1秒
      timer: '' // 到时器-定时器
    }
  },
  watch: {
    sysCurrentTime(val) {
      if (val) {
        this.currentTime = val
        this.loadTime()
      }
    },
    endTime(val) {
      if (val) {
        this.loadTime()
      }
    }
  },
  mounted() {
    this.visibilityChange()// 监听窗口是否获取焦点
    this.loadTime()
  },
  beforeDestroy() {
    clearTimeout(this.timer)
    this.timer = '' // 到时器-定时器置空
  },
  methods: {
    // 当前vue实例创建时-监听窗口是否获取焦点
    visibilityChange() {
      const _this = this
      // 判断是否是IE浏览器
      if (window.ActiveXObject) {
        // 监听当前页面是否获得焦点
        window.attachEvent('onfocus', function() {})
      } else {
        // 其他浏览器
        document.addEventListener('visibilitychange', function() { // 浏览器切换事件
          // visible 窗口聚焦时
          if (document.visibilityState === 'visible') {
            _this.getCurrentTime() // 获取当前时间
          }
        })
      }
    },
    // 当前vue实例销毁时-清除事件
    visibilityDestroy() {
      // 判断是否是IE浏览器
      if (window.ActiveXObject) {
        // 监听当前页面是否获得焦点
        window.detachEvent('onfocus', function() {})
      } else {
        // 其他浏览器
        document.removeEventListener('visibilitychange')
      }
    },
    // 获取当前时间
    getCurrentTime() {
      getTime().then((res) => {
        this.currentTime = res.data.time.replace(/-/g, '/') // 处理ie不兼容日期格式// 系统当前时间
        this.winStCurrentTime = new Date().getTime()// 本地终端时间-定时器起始时间
        // 计算时间
        this.loadTime()
      }).catch(() => {
        this.currentTime = new Date()// 本地当前时间
        this.winStCurrentTime = new Date().getTime()// 本地终端时间-定时器起始时间
        // 计算时间
        this.loadTime()
      })
    },
    // 计算时间
    loadTime() {
      clearTimeout(this.timer)
      this.timer = '' // 到时器-定时器置空
      if (!this.currentTime && !this.endTime) return
      this.count = 0
      const date = this.endTime.replace(/-/g, '/') // 处理ie不兼容日期格式
      this.end = new Date(date).getTime()// 获取列表传的截止时间
      if (this.currentTime) {
        // const currentTime = this.currentTime.replace(/-/g, '/') // 处理ie不兼容日期格式
        const startTime = +new Date(this.startTime.replace(/-/g, '/')) // 处理ie不兼容日期格式
        this.current = new Date(this.currentTime).getTime() // 接受服务器当前时间
        this.msTime.show = true
        setTimeout(() => {
          this.runTime(this.end, this.current, startTime, this.end_message, true, this.index)
        }, 1)
      }
    },
    runTime(endTime, currentTime, startTime, callFun, type, index) {
      /*
       * endTime结束时间
       * currentTime 服务器时间
       * startTime 开始时间
       * callFun 回调函数
       * index 索引
      */

      // 服务器当前时间大于等于结束时间时说明结束时间已到 不继续执行
      if (currentTime >= endTime) {
        callFun(index)
        return false
      }
      // 倒计时执行间隔时间
      let interval = this.interval
      // 标记执行次数
      this.count++
      /*
         * 休眠误差-计算误差
         * 时间差     =    本地终端当前时间     - (本地终端时间-定时器起始时间 + 定时器执行次数 * 倒计时执行间隔时间）
        */
      let difference = new Date().getTime() - (this.winStCurrentTime + this.count * interval - interval)
      const _s = new Date().getTime()
      const msTime = this.msTime
      // 若误差超过执行间隔时间，则立即校正(会出现跳秒的情况)
      if (difference >= 500 || difference <= -500) {
        // 误差超过 500 毫秒或者少于 -500 毫秒 通过 获取服务器时间及时校正
        this.getCurrentTime()
      } else {
        // 取余
        difference = difference % interval
        // 标准间隔时差减去此次的休眠误差，为下一次执行的时间
        interval = interval - difference
        currentTime += this.interval
        // 时间戳 = 活动结束时间 - 系统当前时间
        let timeDistance = endTime - currentTime
        if (timeDistance > 0) {
          this.msTime.show = true
          // 天
          msTime.day = Math.floor(timeDistance / 86400000)
          timeDistance -= msTime.day * 86400000
          // 时
          msTime.hour = Math.floor(timeDistance / 3600000)
          timeDistance -= msTime.hour * 3600000
          // 分
          msTime.minutes = Math.floor(timeDistance / 60000)
          timeDistance -= msTime.minutes * 60000
          // 秒
          msTime.seconds = Math.floor(timeDistance / 1000).toFixed(0)
          timeDistance -= msTime.seconds * 1000
          if (msTime.hour < 10) {
            msTime.hour = '0' + msTime.hour
          }
          if (msTime.minutes < 10) {
            msTime.minutes = '0' + msTime.minutes
          }
          if (msTime.seconds < 10) {
            msTime.seconds = '0' + msTime.seconds
          }

          msTime.hour = msTime.hour + ''
          msTime.minutes = msTime.minutes + ''
          // 定时器
          const _e = new Date().getTime()
          //   js执行误差
          const diffPerFunc = _e - _s
          this.timer = setTimeout(() => {
            this.runTime(endTime, currentTime, startTime, callFun, true, index)
          }, interval - diffPerFunc)
        } else {
          callFun(index)
        }
      }
    },
    end_message(index) {
      if (!this.startTime) {
        this.msTime.show = false
      } else {
        this.msTime.day = '00'
        this.msTime.hour = '00'
        this.msTime.minutes = '00'
        this.msTime.seconds = '00'
      }
      this.$emit('end_callback', this.msTime.show, index)
      if (this.currentTime <= 0) {
        return
      }
    }
  }
}
</script>

<style scoped lang="less">
  // 时间倒计时样式
  .countdown{
    display: inline-block;
    margin-top: 15px;
    p{
      display: inline-block;
    }
    span{
      background: linear-gradient(to bottom, #7EC1EC 0%, #7EC1EC 100%);
      color: white;
      border-radius: 2px;
      font-size: 22px;
      padding: 0 5px;
    }
    span+span{
      margin-left: 2px;
    }
    .dateText{
      font-style: normal;
      margin: 0 5px;
    }
    .time-num{
      position: relative;
      .dateText{
        margin: 0;
        position: absolute;
        bottom: -20px;
        left: 50%;
        transform: translate(-50%);
      }
    }
    .point-of-time{
      height: 100%;
      line-height: 100%;
      color: #333333;
      font-size: 30px;
      padding: 0 3px;
    }
  }
</style>
