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

Laravel中Vue组件@click按钮二次点击报错求助

问题排查与解决:Laravel+Vue模态框二次点击报错

问题描述

在Laravel项目中使用Vue 3开发模态框时,首次点击绑定@click="showModal = true"的按钮功能正常,但再次点击时出现报错。

相关代码

Laravel Blade 代码

<div class="col-span-12 mb-5" id="app">
    <div class="text-end text-base">
        <button type="button" class="text-primary hover:text-secondary" @click="showModal = true">Verify your email
        </button>
    </div>
    <v-modal :open="showModal">
        <form action="" method="post" id="form_verify_email">
            @csrf
            <input type="hidden" name="email" value="{{ auth()->user()->email }}">
            <div>
                <x-user.form.buttons.primary>
                    {{ __('Verify Email') }}
                </x-user.form.buttons.primary>
            </div>
        </form>
    </v-modal>
    <x-user.form.inputs name="email" placeholder="Email" type="email" :value="old('email', auth()->user()->email)"/>
</div>

JS 代码

import { createApp } from 'vue'

import modal from './components/modal.vue'

const app = createApp({
    data() {
        return {
            showModal: false,
        }
    },
})

app.component('v-modal', modal)

app.mount('#app')

Vue 组件代码

<script setup>
import { ref, defineProps,  onMounted } from 'vue'
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'

let { open } = defineProps({
    open: {
        type: Boolean,
        required: true,
    },
})
</script>

问题原因与解决方法

1. 响应式丢失问题

直接解构defineProps返回的对象会丢失响应式。defineProps返回的是响应式Proxy对象,普通解构会提取出原始值而非响应式引用,导致Headless UI的Dialog无法正确监听状态变化。

修复代码:

<script setup>
import { defineProps, defineEmits, toRefs } from 'vue'
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'

const props = defineProps({
    open: {
        type: Boolean,
        required: true,
    },
})
const emit = defineEmits(['update:open'])

// 使用toRefs解构,保留响应式
const { open } = toRefs(props)
</script>

2. 双向绑定缺失,未重置父组件状态

当前组件仅接收open属性,但未在模态框关闭时通知父组件更新showModal状态。导致关闭后showModal仍为true,再次点击按钮时触发组件内部状态冲突。

修复步骤:

  • 在组件中添加关闭事件的发射逻辑
  • 父组件使用v-model:open实现双向绑定

完整修改后的Vue组件(包含TailwindUI模态框结构):

<script setup>
import { defineProps, defineEmits, toRefs } from 'vue'
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'

const props = defineProps({
    open: {
        type: Boolean,
        required: true,
    },
})
const emit = defineEmits(['update:open'])

const { open } = toRefs(props)
</script>

<template>
    <TransitionRoot appear :show="open" as="template">
        <Dialog @close="emit('update:open', false)" class="relative z-10">
            <!-- 背景遮罩 -->
            <TransitionChild
                as="template"
                enter="ease-out duration-300"
                enter-from="opacity-0"
                enter-to="opacity-100"
                leave="ease-in duration-200"
                leave-from="opacity-100"
                leave-to="opacity-0"
            >
                <div class="fixed inset-0 bg-black bg-opacity-25" />
            </TransitionChild>

            <!-- 模态框面板 -->
            <div class="fixed inset-0 overflow-y-auto">
                <div class="flex min-h-full items-center justify-center p-4 text-center">
                    <TransitionChild
                        as="template"
                        enter="ease-out duration-300"
                        enter-from="opacity-0 scale-95"
                        enter-to="opacity-100 scale-100"
                        leave="ease-in duration-200"
                        leave-from="opacity-100 scale-100"
                        leave-to="opacity-0 scale-95"
                    >
                        <DialogPanel class="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
                            <DialogTitle class="text-lg font-medium leading-6 text-gray-900">
                                验证邮箱
                            </DialogTitle>
                            <div class="mt-4">
                                <form action="" method="post" id="form_verify_email">
                                    @csrf
                                    <input type="hidden" name="email" value="{{ auth()->user()->email }}">
                                    <div>
                                        <x-user.form.buttons.primary>
                                            {{ __('Verify Email') }}
                                        </x-user.form.buttons.primary>
                                    </div>
                                </form>
                            </div>
                        </DialogPanel>
                    </TransitionChild>
                </div>
            </div>
        </Dialog>
    </TransitionRoot>
</template>

修改Blade中的模态框调用,使用双向绑定:

<v-modal v-model:open="showModal">
    <!-- 表单内容保持不变 -->
</v-modal>

3. 额外检查

若表单为AJAX提交,需确保提交成功后手动将showModal设为false,避免模态框残留打开状态;若为普通表单提交,页面刷新后状态会重置,无需额外处理。


内容的提问来源于stack exchange,提问作者Nguyễn Tấn Dũng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:44:57