Vue项目需求:点击Header登录按钮弹出模态框且不跳转页面
实现Home页面点击Header的Login按钮显示模态框(不跳转路由)
一、修改Login组件,让外部能控制模态框显示/隐藏
原Login组件的模态框状态是内部私有变量,外部无法控制。需要改为通过props接收状态、emit事件通知关闭的方式,实现父组件对模态框的控制:
<script setup> import { ref, onMounted } from 'vue' import VueCookies from 'vue-cookies' import axios from 'axios'; import { useRouter } from 'vue-router' // 定义props接收外部传递的显示状态 const props = defineProps({ visible: { type: Boolean, default: false } }) // 定义emit事件,通知父组件更新状态 const emit = defineEmits(['update:visible']) const router = useRouter() const baseURL = 'http://localhost:3000/' const userURL = baseURL + "users"; const users = ref(null) const email = ref(null) const password = ref(null) const emailError = ref(null) const passwordError = ref(null) const passwordValidation = ref(false) onMounted(async () => { try { const res = await axios.get(baseURL + 'users'); users.value = res.data; } catch (e) { console.error(e) } }) async function loginSubmit() { // 修复拼写错误:lenght → length,添加空值判断 if(email.value?.length === 0) { emailError.value = "Field is empty" } else { emailError.value = null } if(password.value?.length === 0) { passwordError.value = "Field is empty!"; } else if(password.value.length > 0 && password.value.length < 8) { passwordError.value = "Password is too short!" } else { passwordError.value = null } passwordValidation.value = false; // 遍历用户列表验证登录 for (let i = 0; i < users.value?.length; i++) { if (users.value[i].email === email.value) { if (users.value[i].password === password.value) { passwordValidation.value = true; VueCookies.set('username', users.value[i].username, "120min"); VueCookies.set('email', email.value, "120min"); VueCookies.set('password', password.value, "120min"); VueCookies.set('id', users.value[i].id, "120min"); // 用vue-router跳转代替页面刷新 router.push('/') alert("Login successful"); // 关闭模态框 emit('update:visible', false) break; } } } if (!passwordValidation.value) { passwordError.value = "Incorrect password or username!" } } </script> <template> <!-- 通过v-model:visible实现双向绑定 --> <el-dialog v-model:visible="visible" title="LogIn" width="50%" height="50%" center> <el-form label-position='top' status-icon :label-width="80"> <el-form-item label="Email"> <el-input type="email" id='email' placeholder="Enter Email" v-model="email" /> <div class="input-message" v-if="emailError"><h6>{{emailError}}</h6></div> </el-form-item> <el-form-item label="Password"> <el-input type="password" id='password' placeholder="Enter Password" v-model="password" /> <div class="input-message" v-if="passwordError"><h6>{{passwordError}}</h6></div> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="emit('update:visible', false)">Cancel</el-button> <el-button type="primary" @click="loginSubmit()"> Confirm </el-button> </span> </template> </el-dialog> </template> <style> </style>
二、修改Header组件,引入Login组件并管理模态框状态
移除跳转/login路由的按钮,新增模态框状态变量,引入并渲染Login组件:
<script setup> import { Sunny, Moon } from '@element-plus/icons-vue' import { ref, onMounted } from 'vue' import { RouterLink } from 'vue-router' import { pokeStore } from '../store/store' import axios from 'axios'; import Pokedex from './Pokedex.vue' // 引入Login组件 import Login from './Login.vue' const pokedexVisible = ref(false) // 新增控制登录模态框的状态 const loginModalVisible = ref(false) const PokemonStore = pokeStore(); const allpokemons = ref([]) async function GetAllPokemons() { try { let response = await PokemonStore.getPokemonData(); allpokemons.value = response.data.results; const randomPokemon = allpokemons.value[Math.floor(Math.random() * 151) + 1] console.log(randomPokemon) } catch (error) { throw error; } } GetAllPokemons() </script> <template> <el-header class="navbar"> <div class="navbar-content"> <div> <router-link to="/" custom v-slot="{ navigate }"> <img @click="navigate" role="link" class="logo" src="/src/assets/images/logo.png" /> </router-link> </div> <el-space size="large"> <div> <input class="search" type="text" placeholder="Search pokemon" /> </div> <div> <el-button link><el-icon :size="20"> <Sunny /> </el-icon></el-button> <el-button link><el-icon :size="20"> <Moon /> </el-icon></el-button> </div> <div> <el-button @click="pokedexVisible = true" class="pokedexBtn">Pokedex</el-button> </div> <div> <!-- 保留控制模态框的按钮,移除跳转路由的按钮 --> <el-button @click="loginModalVisible = true" text>LogIn</el-button> </div> </el-space> </div> </el-header> <!-- 渲染Login组件,双向绑定显示状态 --> <Login v-model:visible="loginModalVisible" /> </template> <style scoped> .navbar { background-color: whitesmoke; padding: 5px 30px; position: fixed; top: 0; left: 0; right: 0; opacity: 0.9; } .navbar-content{ align-items: center; justify-content: space-between; display: flex; } .logo{ width: 100%; max-width: 50px; display: block; } .search{ display: block; padding: 5px; border-radius: 4px; font-size: 14px; width: 100%; background-color: transparent; float: right; } .search::placeholder { opacity: 0.5; } .navbar-right{ align-items: center; justify-content: space-between; } .pokedexBtn{ background-color: black; color: whitesmoke; } </style>
三、可选:调整路由配置(移除/login路由)
如果不需要单独的登录页面,可以从路由配置中删除Login相关路由:
import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import Pokedex from '../components/Pokedex.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'Home', component: Home }, { path: '/pokedex', name: 'Pokedex', component: Pokedex }, // 移除Login路由项 // { // path: '/login', // name: 'Login', // component: Login // }, ] }) export default router
核心修改说明
- Login组件:将模态框状态改为外部可控,修复
<script setup>中this的错误使用、拼写错误,用Vue Router跳转代替页面刷新,符合Vue响应式和单向数据流原则。 - Header组件:移除路由跳转按钮,新增模态框状态变量,引入Login组件并绑定状态,点击按钮直接触发模态框显示。
- 路由配置:移除/login路由,避免用户直接访问该路径,确保登录仅通过模态框完成。
内容的提问来源于stack exchange,提问作者Trpimir
相关产品推荐
相关产品推荐

