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

Nuxt.js自定义Alert组件重复触发相同错误时不显示问题求助

Nuxt.js自定义Alert组件重复触发不显示问题

我用Nuxt.js(Vue.js)开发网站,做了个联系表单,用户输入错误或未填必填项时会触发自定义通知,但重复触发相同错误(比如手机号格式错误)时,Alert组件第二次不显示,折腾两天了,求帮忙找原因。


WarningAlert组件代码

<!-- WarningAlert.vue -->
<template>
    <div v-if="show" class="overlay">
      <div class="alert alert-warning" role="alert">
        <div class="container">
          <div class="alert-icon">
            <i class="now-ui-icons ui-1_bell-53"></i>
          </div>
          <strong>{{ strongText }}</strong> {{ message }}
          <button v-if="dismissible" type="button" class="close" @click="hideOverlay" aria-label="Close">
            <span aria-hidden="true">
              <i class="now-ui-icons ui-1_simple-remove"></i>
            </span>
          </button>
        </div>
      </div>
    </div>
  </template>
  
  <script>
  export default {
    props: {
      message: String,
      type: String,
      strongText: String,
      dismissible: Boolean,
    },
    data() {
      return {
        show: false,
      };
    },
    methods: {
      hideOverlay() {
        this.show = false;
      },
      showWarningAlert(message) {
        this.message = message;
        this.show = true;
  
        setTimeout(() => {
          this.hideOverlay();
        }, 5000);
      },
      hideWarningAlert() {
        this.show = false;
        this.message = '';
      },
    },
    watch: {
      message() {
        this.show = true;
        setTimeout(() => {
          this.hideOverlay();
        }, 5000);
      },
    },
    mounted() {
      this.showWarningAlert(this.message);
    },
  };
  </script>
  
  <style scoped>
  .overlay {
    position: fixed;
    top: 50px;
    left: 0;
    width: 100%;
    z-index: 1000;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 20px;
  }
  
  .alert {
    width: 70%;
  }
  </style>

联系表单及组件导入代码

<!-- Alert component -->
<WarningAlert v-if="showWarningAlert" :message="warningMessage" :type="'warning'" :strongText="'Warning!'"
  :dismissible="true" @dismiss="dismissWarningAlert"></WarningAlert>

<!-- Contact Form -->
<div class="main">
  <div class="contact-content">
    <div class="container">
      <div class="row">
        <div class="col-md-5 ml-auto mr-auto">
          <h2 class="title">Senden Sie uns eine Nachricht</h2>
          <p class="description">Wir stehen Ihnen gerne zur Verfügung. Wir werden Ihnen innerhalb eines Werktages
            antworten.
            <br>
            <br>
          </p>
          <form role="form" id="contact-form" @submit.prevent="submitForm" method="post">
            <label>Ihr Name</label>
            <fg-input required placeholder="Name..." v-model="form.firstName"
              addon-left-icon="now-ui-icons users_circle-08"></fg-input>

            <label>Email Adresse</label>
            <fg-input required placeholder="Email..." v-model="form.email"
              addon-left-icon="now-ui-icons users_circle-08" title="Please enter a valid email address"></fg-input>

            <label>Telefon</label>
            <fg-input required placeholder="Telefonnummer..." v-model="form.phone"
              addon-left-icon="now-ui-icons tech_mobile"></fg-input>

            <div class="form-group">
              <label>Ihre Nachricht</label>
              <textarea name="message" class="form-control" id="message" placeholder="Nachricht..." v-model="form.message" rows="6"></textarea>
            </div>

            <div class="submit text-center">
              <button class="btn btn-success btn-rounded" type="submit" round>Senden</button>
            </div>
          </form>

submitForm方法代码

methods: {
    async submitForm() {
      // Initialisation
      this.showWarningAlert = false;
      this.warningMessage = '';

      // Check if all relevant fields filled out
      if (!this.form.firstName || !this.form.email || !this.form.phone) {
        this.showWarningAlert = true;
        this.warningMessage = 'Please fill out all required fields (Name, Email, Phone).';
        return;
      }

      const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
      if (!this.form.email.match(emailRegex)) {
        this.showWarningAlert = true;
        this.warningMessage = 'Please enter a valid email address.';
        return;
      }

      const phoneRegex = /^(\+\d{1,3})?\d{1,14}$/;
      if (!this.form.phone.match(phoneRegex)) {
        this.showWarningAlert = true;
        this.warningMessage = 'Please enter a valid phone number.';
        return;
      }

      // Send formulas if everything is validated
      try {
        const response = await axios.post('http://localhost:3005/send-email', this.form);
        if (response.status === 200) {
          console.log('Email sent successfully');
        }
      } catch (error) {
        console.error('Error sending email:', error);
        // Show warning if error
        this.showWarningAlert = true;
        this.warningMessage = 'Something went wrong. Please contact us via email.';
      } finally {
        // Reset Warning properties before leaving the method
        this.showWarningAlert = false;
        this.warningMessage = '';
      }
    },
    dismissWarningAlert() {
      // Dismiss the warning alert
      this.showWarningAlert = false;
      this.warningMessage = '';
    }
  },

问题根源与修复方案

核心问题

  1. 组件销毁重建与状态冲突:父组件用v-if="showWarningAlert"控制组件挂载,第一次触发错误后,Alert内部5秒后将show设为false,但父组件showWarningAlert仍为true。第二次触发相同错误时,父组件状态无变化,组件不会重新挂载;且两次错误消息相同,Alert内部的message监听不会触发,导致show保持false,无法显示。
  2. 直接修改Props:Alert组件的showWarningAlert方法直接修改this.message(Props),违反Vue单向数据流原则,易引发状态不一致。
  3. finally块强制重置:无论请求成功或失败,finally块都会立即关闭提示,错误提示刚显示就被隐藏。

修复步骤

1. 重构WarningAlert组件

让父组件完全控制显示状态,组件仅负责接收消息和自动隐藏逻辑:

<!-- WarningAlert.vue -->
<template>
  <div v-show="visible" class="overlay">
    <div class="alert alert-warning" role="alert">
      <div class="container">
        <div class="alert-icon">
          <i class="now-ui-icons ui-1_bell-53"></i>
        </div>
        <strong>{{ strongText }}</strong> {{ message }}
        <button v-if="dismissible" type="button" class="close" @click="handleClose" aria-label="Close">
          <span aria-hidden="true">
            <i class="now-ui-icons ui-1_simple-remove"></i>
          </span>
        </button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    message: String,
    strongText: String,
    dismissible: Boolean,
    visible: Boolean,
  },
  data() {
    return {
      timer: null,
    };
  },
  watch: {
    visible(newVal) {
      if (newVal) {
        this.clearTimer();
        this.timer = setTimeout(() => {
          this.$emit('close');
        }, 5000);
      } else {
        this.clearTimer();
      }
    },
  },
  methods: {
    handleClose() {
      this.$emit('close');
      this.clearTimer();
    },
    clearTimer() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
    },
  },
  beforeUnmount() {
    this.clearTimer();
  },
};
</script>

<style scoped>
.overlay {
  position: fixed;
  top: 50px;
  left: 0;
  width: 100%;
  z-index: 1000;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 20px;
}

.alert {
  width: 70%;
}
</style>

2. 修改父组件逻辑

  • 用v-show替代v-if,避免组件频繁销毁重建
  • 移除finally块的强制重置,仅在提交成功后重置状态:
<!-- 父组件中Alert调用 -->
<WarningAlert 
  v-show="showWarningAlert" 
  :message="warningMessage" 
  :strongText="'Warning!'"
  :dismissible="true" 
  @close="dismissWarningAlert"
></WarningAlert>

<!-- submitForm方法修改 -->
methods: {
  async submitForm() {
    this.dismissWarningAlert();

    if (!this.form.firstName || !this.form.email || !this.form.phone) {
      this.warningMessage = '请填写所有必填字段(姓名、邮箱、电话)。';
      this.showWarningAlert = true;
      return;
    }

    const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    if (!this.form.email.match(emailRegex)) {
      this.warningMessage = '请输入有效的邮箱地址。';
      this.showWarningAlert = true;
      return;
    }

    const phoneRegex = /^(\+\d{1,3})?\d{1,14}$/;
    if (!this.form.phone.match(phoneRegex)) {
      this.warningMessage = '请输入有效的手机号码。';
      this.showWarningAlert = true;
      return;
    }

    try {
      const response = await axios.post('http://localhost:3005/send-email', this.form);
      if (response.status === 200) {
        console.log('邮件发送成功');
        this.form = { firstName: '', email: '', phone: '', message: '' };
        this.dismissWarningAlert();
      }
    } catch (error) {
      console.error('邮件发送失败:', error);
      this.warningMessage = '发送失败,请直接通过邮箱联系我们。';
      this.showWarningAlert = true;
    }
  },
  dismissWarningAlert() {
    this.showWarningAlert = false;
    this.warningMessage = '';
  }
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:14:52