InertiaJS表单onSuccess关闭Bootstrap模态框后背景残留问题解决
问题:Laravel+Breeze+Inertia+Vue登录后Bootstrap模态框遮罩残留
我正在开发Laravel项目,基于laravel/breeze + VueJS + Inertia搭建。登录功能采用Bootstrap模态框实现,验证和认证流程均正常,但存在一个问题:认证成功后模态框虽已关闭,背景遮罩却残留页面上。
排查发现,表单提交的onSuccess回调中,emit('closeParent')未执行,但如果换成console.log能正常输出。
以下是相关代码:
Header.vue
<script setup> import { ref } from 'vue'; import Modal from '@/Components/Modal.vue' let thisModal = ref(null) function showModal() { thisModal.value.show() } function closeModal() { thisModal.value.close() } </script> <template> <header class="shadow-sm"> <Modal v-if="! $page.props.auth.user" ref="thisModal"> <template #body> <Login @closeParent="closeModal" @showParent="showModal" /> <Register /> </template> </Modal> </header> </template>
Modal.vue
<script setup> import { onMounted, ref } from 'vue' import { Modal } from 'bootstrap' defineProps({ title: String }) let modalEle = ref(null) let thisModalObj = null onMounted(() => { thisModalObj = new Modal(modalEle.value) }) function _show() { thisModalObj.show() } function _close() { thisModalObj.hide() } defineExpose({ show: _show, close: _close }) </script> <template> <div class="modal fade" tabindex="-1" role="dialog" ref="modalEle"> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <div class="modal-header bg-secondary"> <slot name="header" /> </div> <div class="modal-body tab-content py-4"> <slot name="body" /> </div> <slot name="footer" /> </div> </div> </div> </template>
Login.vue(表单组件)
<script setup> import { Link, useForm } from '@inertiajs/vue3' import Checkbox from '@/Components/Checkbox.vue' import InputError from '@/Components/InputError.vue' import InputLabel from '@/Components/InputLabel.vue' import PrimaryButton from '@/Components/PrimaryButton.vue' import TextInput from '@/Components/TextInput.vue' import PasswordToggle from '@/Components/PasswordToggle.vue' defineProps({ canResetPassword: { type: Boolean, }, status: { type: String, }, }) const form = useForm({ email: '', password: '', remember: false, }) const emit = defineEmits() const submit = () => { form.post(route('login'), { onFinish: () => form.reset('password'), // onBefore 会隐藏模态框 onBefore: () => emit('closeParent'), // onError 会重新显示模态框 onError: () => emit('showParent'), // 期望仅在成功时关闭模态框 onSuccess: () => emit('closeParent') // <- 这里应该是最合理的处理方式,但未生效 }) } </script> <template> <form @submit.prevent="submit" id="login-tab" class="tab-pane fade show active" autocomplete="off" novalidate> <div class="mb-3"> <InputLabel for="email" value="Email" /> <TextInput id="email" type="text" name="email" v-model="form.email" autofocus autocomplete="username" /> <InputError class="fs-sm" :message="form.errors.email" /> </div> <div class="mb-3"> <InputLabel for="password" value="Password" /> <PasswordToggle> <TextInput id="password" type="password" v-model="form.password" autocomplete="current-password" /> </PasswordToggle> <InputError class="fs-sm" :message="form.errors.password" /> </div> <div class="mb-3 d-flex flex-wrap justify-content-between"> <div class="form-check mb-2"> <Checkbox id="remember" name="remember" v-model:checked="form.remember" /> <InputLabel for="remember" value="Remember me" /> </div> <Link v-if="$page.props.app.canResetPassword" class="fs-sm" :href="route('password.request')"> Forgot password? </Link> </div> <PrimaryButton class="btn-shadow d-block w-100" :disabled="form.processing"> <span v-if="form.processing" class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span> <span v-else class="me-2"><i class="ci-unlocked"></i></span> {{ `Sign in` }} </PrimaryButton> </form> </template>
AuthenticatedSessionController.php
/** * Handle an incoming authentication request. */ public function store(LoginRequest $request) // : RedirectResponse { $request->authenticate(); $request->session()->regenerate(); return redirect()->intended(RouteServiceProvider::HOME); }
问题排查思路
当前代码中,点击登录按钮就会触发onBefore隐藏模态框,验证失败时onError重新显示。但我希望仅在认证成功时关闭模态框,但onSuccess里的emit未执行。
推测核心原因:控制器返回的redirect()->intended()会触发Inertia页面跳转,此时Header.vue中的Modal组件因为v-if="! $page.props.auth.user"的条件不再满足,会被立即卸载,导致onSuccess回调还没执行完,组件就已经销毁,emit自然无法生效。同时Bootstrap模态框的遮罩层因为组件卸载前没正确触发hide()方法的完整流程,导致残留。
内容的提问来源于stack exchange,提问作者Richard C. Alvarez
相关产品推荐
相关产品推荐

