You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue组件部署后需点击两次才发起POST请求的问题排查

问题

开发了一款用于OTP验证码验证的Vue组件,验证通过后借助Pinia将OTP存储至localStorage,再跳转至下一个组件使用该验证码:

  • 用户需在4个独立输入框输入4位OTP,组件接收由4个字符串组成的数组,拼接为单个字符串后发送至API验证
  • 本地环境(localhost)功能完全正常,但部署到服务器后出现异常:用户输入OTP点击「Verificar」按钮无响应,需再次输入并点击第二次才会触发验证请求
  • 曾尝试用watch监听otp数组,在输入完成时执行拼接,点击按钮仅发起POST请求,本地可行但服务器端异常依旧

问题分析

  1. 表单默认提交行为冲突:验证按钮类型为submit,点击时会触发表单默认提交行为(页面刷新/组件重置),服务器环境下网络延迟会放大这个冲突,导致第一次点击实际触发的是表单提交而非自定义验证逻辑
  2. 重复触发验证逻辑:watch监听otp数组自动调用crearCuenta(),同时按钮点击也会触发该方法,服务器环境下异步请求的状态更新延迟,导致otpVerified状态未及时同步,出现点击无响应
  3. 定时器重复初始化:created和mounted钩子都调用了startCountdown(),会创建两个并行定时器,导致组件状态混乱,影响交互逻辑

修复方案

  1. 阻止表单默认提交:将验证按钮类型改为button,避免触发表单默认提交行为;或在点击事件中添加event.preventDefault()
  2. 统一验证触发逻辑:移除watch自动提交逻辑,仅保留按钮点击触发验证,避免重复调用API导致状态不同步
  3. 修复定时器重复创建:只在mounted或created中调用一次startCountdown(),避免多个定时器同时运行
  4. 优化异步状态处理:在API请求完成后直接处理跳转逻辑,避免依赖otpVerified状态的延迟更新

修改后的代码示例

<template>
  <form class="otp-Form">
    <span class="mainHeading">Entrar OTP</span>
    <div>
      <p class="resendNote font-poppins">¿No recibiste el codigo?</p>
      <p v-if="countdownText === ''" class="countdownText text-brand-primary">
        Intentar de nuevo en
        <span class="font-bold text-red-600">{{ countdown }}</span> segundos
      </p>
      <button
        v-else
        class="text-brand-primary font-poppins"
        style="padding-left: 3em"
        :disabled="countdown > 0"
        @click="reSendOTP()"
        type="button"
      >
        {{ countdownText }}
      </button>
    </div>
    <p class="otpSubheading">
      Hemos enviado un codigo de verificacion a tu correo electronico.
    </p>
    <p class="text-xs">
      No olvides checar su carpeta
      <span class="text-blue-800 line-clamp-1 text-center font-bold text-sm"
        >"No Deseado"</span
      >
    </p>
    <div class="inputContainer">
      <input
        id="otp-input1"
        v-model="otp[0]"
        required
        maxlength="1"
        type="text"
        class="otp-input"
        @input="focusNextInput($event, 1)"
      />
      <input
        id="otp-input2"
        v-model="otp[1]"
        required
        maxlength="1"
        type="text"
        class="otp-input"
        @input="focusNextInput($event, 2)"
      />
      <input
        id="otp-input3"
        v-model="otp[2]"
        required
        maxlength="1"
        type="text"
        class="otp-input"
        @input="focusNextInput($event, 3)"
      />
      <input
        id="otp-input4"
        v-model="otp[3]"
        required
        maxlength="1"
        type="text"
        class="otp-input"
      />
    </div>
    <!-- 修改按钮类型为button,避免触发表单默认提交 -->
    <button class="verifyButton bg-brand-primary" type="button" @click="handleVerify">
      Verificar
    </button>
  </form>
</template>

<script>
import axios from "axios";
import config from "../../../environment";
import { useOtpCodeStore } from "@/stores/otpCodeStore";

export default {
  name: "WaitOTPMail",
  props: {
    gender: {
      type: String,
      default: "",
      required: false,
    },
    name: {
      type: Object,
      default: null,
      required: false,
    },
  },
  data() {
    return {
      countdown: 120,
      countdownText: "",
      otp: ["", "", "", ""],
      code: "",
    };
  },
  // 移除自动提交的watch逻辑,仅保留必要的生命周期钩子
  mounted() {
    this.startCountdown();
  },
  methods: {
    // 新增输入框自动聚焦逻辑,提升用户体验
    focusNextInput(event, index) {
      if (event.target.value.length === 1 && index < 4) {
        document.getElementById(`otp-input${index + 1}`).focus();
      }
    },
    reSendOTP() {
      this.$router.push("/newuser");
    },
    crearCuenta() {
      this.code = this.otp.join("");
      const verificarOTP = { check_otp: this.code };
      
      const otpCodeStore = useOtpCodeStore();
      otpCodeStore.setOtpCode(this.code);

      return axios.post(
        `${config.activeMod.API_BASE_URL}/api/new/verify-sms-otp`,
        verificarOTP
      );
    },
    handleVerify() {
      // 先校验OTP是否完全输入
      if (!this.otp.every(digit => digit !== "")) {
        alert("Por favor completa el código OTP");
        return;
      }
      
      this.crearCuenta()
        .then((response) => {
          if (response.status === 202) {
            this.moveOn();
          } else {
            alert("Código OTP incorrecto");
          }
        })
        .catch((error) => {
          console.error("Error al verificar OTP: ", error);
          alert("Error en la verificación, intenta nuevamente");
        });
    },
    moveOn() {
      this.$router.push("/create-password");
    },
    startCountdown() {
      const timer = setInterval(() => {
        if (this.countdown > 0) {
          this.countdown--;
        } else {
          clearInterval(timer);
          this.countdownText = "Intentar de Nuevo";
        }
      }, 1000);
    },
  },
};
</script>

内容的提问来源于stack exchange,提问作者Goktug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:51:11