<template>
  <div style="position: relative;">
    <!-- 图片验证码部分 -->
    <div
      v-if="type === '2'"
      class="verify-img-out"
      :style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
    >
      <div
        class="verify-img-panel"
        :style="{width: setSize.imgWidth, height: setSize.imgHeight}"
      >
        <!-- 显示背景图片 -->
        <img
          :src="'data:image/png;base64,'+backImgBase"
          alt=""
          style="width:100%;height:100%;display:block"
        />

        <!-- 刷新按钮 -->
        <div
          v-show="showRefresh"
          class="verify-refresh"
          @click="refresh"
        >
          <i class="iconfont icon-refresh"/>
        </div>

        <!-- 提示信息 -->
        <transition name="tips">
                    <span
                      v-if="tipWords"
                      class="verify-tips"
                      :class="passFlag ?'suc-bg':'err-bg'"
                    >{{tipWords}}</span>
        </transition>
      </div>
    </div>

    <!-- 公共部分（滑动条） -->
    <div
      class="verify-bar-area"
      :style="{width: setSize.imgWidth, height: barSize.height, 'line-height':barSize.height}"
    >
      <!-- 滑动条上的消息文本 -->
      <span class="verify-msg" v-text="text"/>

      <!-- 左边进度条 -->
      <div
        class="verify-left-bar"
        :style="{width: (leftBarWidth !== undefined)? leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transition: transitionWidth}"
      >
        <!-- 完成后的消息文本 -->
        <span class="verify-msg" v-text="finishText"/>

        <!-- 滑动块 -->
        <div
          class="verify-move-block"
          :style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"
          @touchstart="start"
          @mousedown="start"
        >
          <!-- 滑动块内的图标 -->
          <i
            :class="['verify-icon iconfont', iconClass]"
            :style="{color: iconColor}"
          />

          <!-- 当 type 为 '2' 时显示的小图块 -->
          <div
            v-if="type === '2'"
            class="verify-sub-block"
            :style="{
                            width: Math.floor(parseInt(setSize.imgWidth)*47/310) + 'px',
                            height: setSize.imgHeight,
                            top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
                            'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
                        }"
          >
            <img
              :src="'data:image/png;base64,'+blockBackImgBase"
              alt=""
              style="width:100%;height:100%;display:block;-webkit-user-drag:none;"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script type="text/babel">
/**
 * VerifySlide
 * @description 滑块验证组件，用于用户交互验证，防止机器人操作。
 */
import { aesEncrypt } from './../utils/ase' // AES加密函数，用于加密提交的数据
import { resetSize } from './../utils/util' // 重置大小工具函数
import { reqCheck, reqGet } from './../api/index' // 请求接口，包含检查和获取验证码图片的API
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue'

export default {
  name: 'VerifySlide',
  props: {
    captchaType: { // 验证码类型
      type: String
    },
    type: { // 验证码种类，默认为'1'
      type: String,
      default: '1'
    },
    mode: { // 显示模式：弹出式或固定式
      type: String,
      default: 'fixed'
    },
    vSpace: { // 垂直间距
      type: Number,
      default: 5
    },
    explain: { // 提示文字
      type: String,
      default: '向右滑动完成验证'
    },
    imgSize: { // 背景图尺寸
      type: Object,
      default () {
        return {
          width: '310px',
          height: '155px'
        }
      }
    },
    blockSize: { // 滑块尺寸
      type: Object,
      default () {
        return {
          width: '50px',
          height: '50px'
        }
      }
    },
    barSize: { // 进度条尺寸
      type: Object,
      default () {
        return {
          width: '310px',
          height: '40px'
        }
      }
    }
  },
  setup (props) {
    const { mode, captchaType, type, blockSize, explain } = toRefs(props)
    const { proxy } = getCurrentInstance()

    // 定义响应式数据
    const secretKey = ref('') // 加密秘钥
    const passFlag = ref(false) // 验证通过标识
    const backImgBase = ref('') // 验证码背景图片base64编码
    const blockBackImgBase = ref('') // 滑块背景图片base64编码
    const backToken = ref('') // 后端返回的唯一token值
    const startMoveTime = ref(0) // 移动开始的时间戳
    const endMovetime = ref(0) // 移动结束的时间戳
    const tipsBackColor = ref('') // 提示词的背景颜色
    const tipWords = ref('') // 提示语
    const text = ref(explain.value) // 默认提示文本
    const finishText = ref('') // 完成后的文本

    const setSize = reactive({
      imgHeight: 0,
      imgWidth: 0,
      barHeight: 0,
      barWidth: 0
    }) // 图片和进度条尺寸对象

    const top = ref(0) // 滑块顶部位置
    const left = ref(0) // 滑块左侧位置
    const moveBlockLeft = ref(undefined) // 滑块移动后的位置
    const leftBarWidth = ref(undefined) // 左侧进度条宽度

    // 定义样式相关变量
    const moveBlockBackgroundColor = ref(undefined) // 滑块背景色
    const leftBarBorderColor = ref('#ddd') // 左侧进度条边框颜色
    const iconColor = ref(undefined) // 图标颜色
    const iconClass = ref('icon-right') // 图标类名

    const status = ref(false) // 是否正在拖拽
    const isEnd = ref(false) // 是否已经完成验证
    const showRefresh = ref(true) // 是否显示刷新按钮
    const transitionLeft = ref('') // 左移动画
    const transitionWidth = ref('') // 宽度变化动画
    const startLeft = ref(0) // 开始拖拽时滑块的left值

    const barArea = computed(() => {
      return proxy.$el.querySelector('.verify-bar-area') // 获取进度条区域DOM元素
    })

    function init () {
      // 初始化设置默认提示文本，并获取新的验证码图片
      getPictrue()
      // 确保所有尺寸正确设置后触发父组件的ready事件
      nextTick(() => {
        const sizeInfo = resetSize(proxy)
        Object.assign(setSize, sizeInfo)
        proxy.$parent.$emit('ready', proxy)
      })
      // 添加触摸和鼠标事件监听器，确保能够处理移动端和PC端事件
      window.addEventListener('touchmove', move)
      window.addEventListener('mousemove', move)
      window.addEventListener('touchend', end)
      window.addEventListener('mouseup', end)
    }

    // 监听type属性变化重新初始化
    watch(type, () => {
      init()
    })

    // 组件挂载完成后初始化
    onMounted(() => {
      init()
      // 禁用选择以防止用户选中页面内容
      proxy.$el.onselectstart = function () {
        return false
      }
    })

    // 鼠标按下事件处理函数
    function start (e) {
      e = e || window.event
      let x = !e.touches ? e.clientX : e.touches[0].pageX // 根据环境获取点击坐标
      startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left) // 计算初始left值
      startMoveTime.value = Date.now() // 记录开始时间
      if (!isEnd.value) {
        text.value = '' // 清空提示文本
        // 设置拖拽过程中的样式
        moveBlockBackgroundColor.value = '#337ab7'
        leftBarBorderColor.value = '#337AB7'
        iconColor.value = '#fff'
        status.value = true // 更新状态为拖拽中
      }
    }

    // 鼠标移动事件处理函数
    function move (e) {
      e = e || window.event
      let x = !e.touches ? e.clientX : e.touches[0].pageX // 根据环境获取当前坐标
      if (status.value && !isEnd.value) {
        const bar_area_left = barArea.value.getBoundingClientRect().left
        let move_block_left = x - bar_area_left // 计算滑块相对于父元素的left值
        // 边界检测，确保滑块不会超出进度条范围
        if (move_block_left >= barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2) {
          move_block_left = barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2
        }
        if (move_block_left <= 0) {
          move_block_left = parseInt(blockSize.value.width) / 2
        }
        // 更新滑块位置和进度条宽度
        moveBlockLeft.value = `${move_block_left - startLeft.value}px`
        leftBarWidth.value = `${move_block_left - startLeft.value}px`
      }
    }

    // 鼠标松开
    function end () {
      // 设置结束时间戳
      endMovetime.value = Date.now()

      // 判断是否满足验证条件：status为true且isEnd为false
      if (status.value && isEnd.value === false) {
        // 解析滑块移动的像素距离，并根据设定的图片宽度调整该距离到合适的比例
        let moveLeftDistance = Number.parseInt((moveBlockLeft.value || '').replace('px', ''))
        moveLeftDistance = moveLeftDistance * 310 / Number.parseInt(setSize.imgWidth)

        // 构建要发送的数据对象，包含验证码类型、点的位置信息（x坐标为滑动距离，y固定值5.0），以及后端令牌
        const data = {
          captchaType: captchaType.value, // 验证码类型
          pointJson: secretKey.value
            ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value) // 如果存在密钥，则加密位置信息
            : JSON.stringify({ x: moveLeftDistance, y: 5.0 }), // 否则直接序列化位置信息
          token: backToken.value // 后端提供的令牌
        }

        // 发送请求进行验证
        reqCheck(data).then(res => {
          // 如果返回的状态码是'0000'，表示验证成功
          if (res.data.repCode === '0000') {
            // 修改UI样式以反映成功的状态
            moveBlockBackgroundColor.value = '#5cb85c'
            leftBarBorderColor.value = '#5cb85c'
            iconColor.value = '#fff'
            iconClass.value = 'icon-check'
            showRefresh.value = false
            isEnd.value = true

            // 根据模式不同设置不同的行为
            if (mode.value === 'pop') {
              setTimeout(() => {
                proxy.$parent.clickShow = false
                refresh() // 成功后刷新验证码
              }, 1500)
            }

            passFlag.value = true // 标记为通过验证
            tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s验证成功` // 显示用时和结果提示

            // 构造最终的验证码验证信息
            const captchaVerification = secretKey.value
              ? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value) // 加密
              : `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}` // 不加密

            // 延迟后关闭当前窗口并触发父组件的成功事件
            setTimeout(() => {
              tipWords.value = ''
              proxy.$parent.closeBox()
              proxy.$parent.$emit('success', { captchaVerification })
            }, 1000)

          } else {
            // 如果验证失败，修改UI样式以反映错误状态，并准备重试
            moveBlockBackgroundColor.value = '#d9534f'
            leftBarBorderColor.value = '#d9534f'
            iconColor.value = '#fff'
            iconClass.value = 'icon-close'
            passFlag.value = false

            // 延迟后刷新验证码
            setTimeout(() => {
              refresh()
            }, 1000)

            // 触发父组件的错误事件
            proxy.$parent.$emit('error', proxy)
            tipWords.value = '验证失败'

            // 清除提示文字
            setTimeout(() => {
              tipWords.value = ''
            }, 1000)
          }
        })

        status.value = false // 更新状态为已完成验证尝试
      }
    }

    const refresh = () => {
      // 设置 showRefresh 的值为 true，这可能会触发显示一个刷新图标或按钮，
      // 允许用户知道他们可以刷新验证或者已经开始刷新过程。
      showRefresh.value = true;

      // 清空 finishText 的值，finishText 可能是当验证成功后显示给用户的文本信息。
      // 现在我们清除它，为下一次验证做准备。
      finishText.value = '';

      // 设置 transitionLeft 的值，定义了左侧滑块移动的动画效果持续时间为0.3秒。
      // 这将使滑块平滑地回到起始位置。
      transitionLeft.value = 'left .3s';

      // 将 moveBlockLeft 的值设置为 0，这会将滑块的位置重置到最左边。
      moveBlockLeft.value = 0;

      // 设置 leftBarWidth 的值为 undefined。这可能是为了确保进度条宽度能够正确重置。
      // 如果之前设置了具体的宽度，在这里取消设置以恢复默认行为。
      leftBarWidth.value = undefined;

      // 设置 transitionWidth 的值，定义了左侧进度条宽度变化的动画效果持续时间为0.3秒。
      // 这将使进度条宽度的变化看起来更加流畅。
      transitionWidth.value = 'width .3s';

      // 设置左栏边框的颜色为浅灰色（#ddd），这是滑块未开始拖动时的默认颜色。
      leftBarBorderColor.value = '#ddd';

      // 设置滑块背景色为白色（#fff），这是滑块未开始拖动时的默认颜色。
      moveBlockBackgroundColor.value = '#fff';

      // 设置图标的颜色为黑色（#000），这可能是滑块内箭头或其他指示图标的颜色。
      iconColor.value = '#000';

      // 设置图标类名为 'icon-right'，这可能是指向右侧的箭头图标，
      // 表示用户应该向右拖动滑块进行验证。
      iconClass.value = 'icon-right';

      // 设置 isEnd 的值为 false，表示验证过程尚未结束，用户可以继续尝试验证。
      isEnd.value = false;

      // 调用 getPictrue 函数，这可能是用来获取新的验证码图片。
      // 在刷新验证时，更换验证码图片是必要的，以防止用户使用旧的验证码通过验证。
      getPictrue();

      // 使用 setTimeout 来延迟执行以下操作300毫秒：
      // - 清除 transitionWidth 和 transitionLeft 的值，停止任何正在进行的CSS过渡效果。
      // - 将 text 的值设置为 explain 的值，这可能是为了更新验证区域中的提示文本。
      //   例如，解释用户接下来应该做什么。
      setTimeout(() => {
        transitionWidth.value = '';
        transitionLeft.value = '';
        text.value = explain.value;
      }, 300);
    };

    // 请求背景图片和验证图片
    function getPictrue () {
      // 构建请求数据对象，包含必要的参数以获取新的验证码图片
      const data = {
        captchaType: captchaType.value, // 验证码类型，可能是滑块验证、点击验证等
        clientUid: localStorage.getItem('b2cSlider'), // 客户端唯一标识符，从本地存储中获取
        ts: Date.now() // 当前时间戳，用来确保每次请求都是唯一的，防止缓存
      };

      // 发送请求以获取新的验证码图片信息
      reqGet(data).then(res => {
        // 检查服务器响应的状态码是否为'0000'，表示成功获取验证码图片
        if (res.data.repCode === '0000') {
          // 更新背景图片和滑块图片的Base64编码字符串
          backImgBase.value = res.data.repData.originalImageBase64; // 背景图的Base64编码
          blockBackImgBase.value = res.data.repData.jigsawImageBase64; // 滑块图的Base64编码

          // 更新后端令牌和密钥，这些可能用于后续的验证请求
          backToken.value = res.data.repData.token;
          secretKey.value = res.data.repData.secretKey;

        } else {
          // 如果获取验证码图片失败，设置提示文字显示错误信息
          tipWords.value = res.data.repMsg; // 显示来自服务器的错误消息
        }
      });
    }
    return {
      secretKey, // 后端返回的ase加密秘钥
      passFlag, // 是否通过的标识
      backImgBase, // 验证码背景图片
      blockBackImgBase, // 验证滑块的背景图片
      backToken, // 后端返回的唯一token值
      startMoveTime, // 移动开始的时间
      endMovetime, // 移动结束的时间
      tipsBackColor, // 提示词的背景颜色
      tipWords,
      text,
      finishText,
      setSize,
      top,
      left,
      moveBlockLeft,
      leftBarWidth,
      // 移动中样式
      moveBlockBackgroundColor,
      leftBarBorderColor,
      iconColor,
      iconClass,
      status, // 鼠标状态
      isEnd,		// 是够验证完成
      showRefresh,
      transitionLeft,
      transitionWidth,
      barArea,
      refresh,
      start
    }
  }
}
</script>
