现在的前端界面如下图:显示“当前用户”是静态不可修改的,并且没有实现“个人中心”和“退出登录”按钮的逻辑,所以接下来就要对前后端进行修改实现“当前用户”真正显示的是当前用户的名字。
一.简要概述
后端部分:
-
用户登录和信息获取 API:
- 登录认证控制器(例如
AuthController
):- 确保登录成功后,返回用户的基本信息(如姓名、角色、token等)。
- 用户信息控制器(例如
UserController
):- 创建一个 API 来获取当前登录用户的信息,例如
/api/user/me
。这个 API 需要返回用户的详细信息,包括姓名、角色等。
- 创建一个 API 来获取当前登录用户的信息,例如
- 安全配置类(例如
SecurityConfig
):- 配置安全策略以确保只有登录用户可以访问用户信息的 API。
- 登录认证控制器(例如
-
退出登录 API(可选):
- 如果你使用会话管理(如 Spring Security),可以创建一个登出 API,清除会话或 token。
- 如果使用 JWT,通常不需要后端支持的登出逻辑,前端只需清除 token 即可。
前端部分:
-
获取并显示用户名:
- Vuex 状态管理(或类似的全局状态管理):
- 在登录成功后,将用户的姓名、token 等信息保存到 Vuex 状态管理或 localStorage。
- 头部组件(
Header.vue
):- 在
mounted
生命周期钩子中,通过 Vuex 或 localStorage 获取用户信息,并将用户名显示在右上角。
- 在
- Vuex 状态管理(或类似的全局状态管理):
-
退出登录逻辑:
- 头部组件(
Header.vue
):- 在
handleCommand
方法中处理登出逻辑,清除 Vuex 或 localStorage 中的用户信息,然后使用this.$router.push('/login')
跳转到登录页面。
- 在
- 头部组件(
二.后端代码编写
后端中已经编写了登录的方法(根据账号返回User对象),使用gpt让我重新创建一个方法(根据id返回User对象),亲测没用,因为前端在登录的时候就已经获得了对象,所以不用再创建一个方法,controller代码如下:
package com.example.controller; //登录验证类 import com.example.config.ApiResponse; import com.example.entity.User; import com.example.service.UserService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/auth") public class UserController { @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<ApiResponse<?>> login(@RequestBody LoginRequest loginRequest) { User user = userService.selectByPhone2(loginRequest.getPhoneNumber()); if (user != null && userService.checkPassword(loginRequest.getPassword(), user.getUserPassword())) { return ResponseEntity.ok(ApiResponse.success("登录成功",user)); } else { return ResponseEntity.status(401).body(ApiResponse.error("登录失败")); } } // 登录请求的内部类 @Data public static class LoginRequest { private String phoneNumber; private String password; } // @GetMapping("/profile") // public ResponseEntity<ApiResponse<?>> getUserProfile(@RequestParam Integer userId) { // User user = userService.selectById2(userId); // if (user != null) { // return ResponseEntity.ok(ApiResponse.success("获取用户信息成功", user)); // } else { // return ResponseEntity.status(404).body(ApiResponse.error("未找到用户")); // } // } }
三.前端代码编写
Login.vue:
<template> <div class="login-container"> <div class="form-container"> <h2>登录界面</h2> <form @submit.prevent="login"> <div class="form-group"> <label for="phoneNumber">账号:</label> <input type="text" id="phoneNumber" v-model="phoneNumber" placeholder="请输入登录账号" required /> </div> <div class="form-group"> <label for="password">密码:</label> <input type="password" id="password" v-model="password" placeholder="请输入密码" required /> </div> <button type="submit">登录</button> </form> <p v-if="errorMessage" class="error">{{ errorMessage }}</p> </div> </div> </template> <script> import axios from 'axios'; export default { data() { return { phoneNumber: '', password: '', errorMessage: null, }; }, methods: { async login() { try { const response = await axios.post('http://localhost:8081/api/auth/login', { phoneNumber: this.phoneNumber, password: this.password, }); // console.log('登录响应:', response.data.data); // 查看响应数据 // console.log('登录响应:', response.data.data.userName); // 查看响应数据 if (response.data.success) { this.$emit('login-success', { picture: response.data.data.userPicture, // 用户头像链接 name: response.data.data.userName // 用户名字 });//多返回userPicture和userName,这里是双层数组,使用console.log可以发现 this.$message.success("登录成功"); } else { this.errorMessage = response.data.message; } } catch (error) { this.errorMessage = '登录失败,请检查账号和密码'; } }, }, }; </script> <style scoped> .login-container { position: relative; width: auto; height: 100vh; background-image: url('@/images/2.jpg'); background-size: cover; background-position: center; display: flex; justify-content: flex-end; align-items: center; padding-right: 10%; } .form-container { width: 300px; padding: 30px; border: 1px solid rgba(0, 0, 0, 0.2); /* 边框颜色也设置为半透明 */ border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); background-color: rgba(255, 255, 255, 0.95); /* 使用rgba设置背景色为半透明 */ position: absolute; top: 50%; right: 15%; transform: translateY(-50%); } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input[type="text"], input[type="password"] { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; } button { width: 100%; padding: 10px; background-color: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background-color: #0056b3; } .error { color: red; margin-top: 15px; text-align: center; } </style>
App.vue
<template> <div> <el-container v-if="isLoggedIn" style="height: 100%;"> <el-header style="width: 100% ;padding: 0"> <Header :userPicture="userPicture" :userName="userName" /> </el-header> <div class="divider"></div> <!-- 分割线 --> <el-container style="width: 100%"> <el-aside width="200px"> <Sidebar @select="updateContent" /> </el-aside> <el-main> <Content :selectedSection="selectedSection" /> </el-main> </el-container> </el-container> <div v-else> <Login @login-success="handleLoginSuccess" /> </div> </div> </template> <script> import Header from './components/Header.vue'; import Sidebar from './components/Sidebar.vue'; import Content from './components/Content.vue'; import Login from './components/Login.vue'; export default { components: { Header, Sidebar, Content, Login, }, data() { return { selectedSection: '0', isLoggedIn: false, // 登录状态 userPicture:'', // 用户头像 userName: '' // 用户名字 }; }, methods: { updateContent(index) { this.selectedSection = index; }, handleLoginSuccess(userData) {//接收Login返回的数据并传给Header this.isLoggedIn = true; // 登录成功后更新状态 this.userPicture = userData.picture; // 假设 userData 包含头像链接 this.userName = userData.name; // 假设 userData 包含用户名 this.$nextTick(() => { console.log('头像链接:', this.userPicture); // 查看更新后的数据 console.log('用户名:', this.userName); // 查看更新后的数据 }); } } }; </script>
Header.vue:
<!-- 头部 --> <template> <div class="header"> <img src="@/images/1.jpg" alt="Logo" class="logo" /> <div class="title">小菜家教平台</div> <el-dropdown @command="handleCommand"> <span class="el-dropdown-link"> <img :src="userPicture" class="user-picture" alt="用户头像" /> {{ userName }} <el-icon class="el-icon--right"><arrow-down /></el-icon> </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="profile">个人中心</el-dropdown-item> <el-dropdown-item command="logout">退出登录</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> </template> <script> export default { props: { userPicture: { type: String, default: () => require('@/images/3.jpg') }, userName: { type: String, default: '用户' } }, methods: { handleCommand(command) { if (command === 'profile') { alert('个人中心'); } else if (command === 'logout') { alert('退出登录'); } } }, mounted() { console.log('Header 组件接收到的头像链接:', this.userPicture); console.log('Header 组件接收到的用户名:', this.userName); } } </script> <style scoped> .header { display: flex; justify-content: space-between; align-items: center; /* 垂直居中对齐 */ padding: 0 30px; height: 60px; /* 适当调整头部高度 */ background-color: slateblue; box-sizing: border-box; /* 包含边框和内边距在宽度和高度中 */ } .logo { width: 35px; height: 35px; border-radius: 50%; margin-right: 10px; border: 1px solid white; } .title { font-size: 22px; font-weight: 700; color: white; margin-right: auto; } .user-picture { width: 30px; height: 30px; border-radius: 50%; margin-right: 10px; object-fit: cover; /* 确保图片按比例显示 */ } .el-dropdown-link { cursor: pointer; color: white; display: flex; align-items: center; font-size: 15px; } .el-dropdown-link:focus { outline: none; } </style>