文章大纲
加载中...

待办清单项目13:重构登录功能

5/16/2025 177 阅读
待办清单项目13:重构登录功能

完成退出登录的功能

1.打开前端项目,在终端窗口运行npm install pinia命令,安装pinia

2.在src目录下创建一个pinia.js文件,写上如下图所示的代码:

3.打开main.js文件,在如图所示的位置添加框中的代码。

4.在src目录中创建一个store目录,然后在store目录中创建一个userStore.js文件

该文件的代码如下:

import { defineStore } from "pinia";

export const defineUser = defineStore("loginUser", {
  state: () => ({
    uid: null,
    username: null,
  }),
  getters: {
},
  actions: {
    setUid(uid) {
      this.uid = uid;
    },
    setUsername(username) {
      this.username = username;
    }
  },
});

5.在store目录中创建todoStore.js文件,粘贴下面的代码

import { defineStore } from "pinia";

export const defineTodoList = defineStore("todoList", {
  state: () => ({
    list: [
      {
        sid: 1,
        uid: 1,
        title: "吃饭",
        completed: 0
      },
      {
        sid: 2,
        uid: 1,
        title: "睡觉",
        completed: 1
      },
      {
        sid: 3,
        uid: 2,
        title: "打豆豆",
        completed: 0
      },
      {
        sid: 4,
        uid: 2,
        title: "打球",
        completed: 1
      }
    ]
  }),
  getters: {
    
 },
  actions: {
    clear() {
      this.list = [];
    }
  },
});

6.打开Header.vue文件,修改代码如下:

<script setup>
  import {defineUser } from '../store/userStore.js' // 导入用户状态管理模块
  import {defineTodoList} from '../store/todoStore.js' // 导入待办事项状态管理模块
  import { useRouter } from 'vue-router' // 导入路由模块
  const router = useRouter() // 获取路由实例
  let sysUser = defineUser() // 获取用户状态管理实例
  let todoList = defineTodoList() // 获取待办事项状态管理实例
  const logout = () => {
    // 退出登录,清空用户信息,重置待办事项列表
    sysUser.uid = null
    sysUser.username = null
    todoList.clear()
    // 跳转到登录页面
    router.push('/login')
  }
</script>

<template>
  <header class="header">
    <div class="logo">
      <!-- 标题 -->
      <h1>待办事项管理系统</h1>
      <!-- 副标题 -->
      <p>欢迎使用我们的待办事项管理系统</p>
    </div>
    
    <div class="actions">
      <!-- 链接到待办事项页面的按钮 -->
      <router-link to="/todolist" class="btn">待办事项</router-link>
      
      <!-- 如果用户未登录,则显示登录和注册按钮 -->
      <template v-if="sysUser.uid == null">
        <router-link to="/login" class="btn btn-primary">登录</router-link>
        <router-link to="/regist" class="btn btn-outline">注册</router-link>
      </template>
      
      <!-- 如果用户已登录,则显示用户名和退出登录按钮 -->
      <template v-else>
        <span class="username">{{ sysUser.username }}</span>
        <button class="btn btn-danger" @click="logout">退出登录</button>
      </template>
    </div>
  </header>
</template>

<style scoped>
.header {
  display: flex;
  justify-content: space-between; /* 使子元素在主轴上两端对齐 */
  align-items: center; /* 使子元素在交叉轴上居中对齐 */
  padding: 1rem 2rem; /* 设置内边距 */
  background-color: #f8f9fa; /* 设置背景颜色 */
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
}

.logo h1 {
  margin: 0; /* 去除默认的外边距 */
  font-size: 1.5rem; /* 设置字体大小 */
  color: #333; /* 设置字体颜色 */
}

.logo p {
  margin: 0.25rem 0 0; /* 设置外边距 */
  font-size: 0.9rem; /* 设置字体大小 */
  color: #666; /* 设置字体颜色 */
}

.actions {
  display: flex; /* 使用Flex布局 */
  gap: 0.75rem; /* 设置子元素之间的间距 */
  align-items: center; /* 使子元素在交叉轴上居中对齐 */
}

.username {
  font-weight: 500; /* 设置字体粗细 */
  margin-right: 0.5rem; /* 设置右边距 */
  color: #333; /* 设置字体颜色 */
}

.btn {
  text-decoration: none;  /* 去除下划线 */
  padding: 0.5rem 1rem; /* 设置内边距 */
  border: none; /* 去除边框 */
  border-radius: 4px; /* 设置圆角 */
  cursor: pointer; /* 设置鼠标悬停时的光标样式 */
  font-size: 0.9rem; /* 设置字体大小 */
  transition: all 0.2s; /* 设置过渡效果 */
}

.btn:hover {
  opacity: 0.9; /* 设置鼠标悬停时的透明度 */
}

.btn-primary {
  background-color: #4361ee; /* 设置按钮背景颜色 */
  color: white; /* 设置按钮字体颜色 */
}

.btn-outline {
  background-color: transparent; /* 设置按钮背景颜色为透明 */
  border: 1px solid #4361ee; /* 设置按钮边框 */
  color: #4361ee; /* 设置按钮字体颜色 */
}

.btn-danger {
  background-color: #ef476f; /* 设置按钮背景颜色 */
  color: white; /* 设置按钮字体颜色 */
}
</style>

7.打开IDEA后端代码,找到sysUserController文件,把login函数里面最后一个else里面的代码改成下面这样:

8.修改前端Login.vue文件代码如下:

<script setup>

import { ref, reactive } from 'vue'
import request from '../utils/request'
import { useRouter } from 'vue-router'
import {defineUser } from '../store/userStore.js' // 导入用户状态管理模块
let sysUser = defineUser() // 实例化用户状态管理模块

const router = useRouter()

let loginUser = reactive({
    username: "",
    user_pwd: ""
})

let usernameMsg = ref("")
let userPwdMsg = ref("")


function checkUsername(){
  // 用正则表达式验证用户名
  // 用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位
  const reg = /^[A-Za-z](?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{7,}$/;
  if (reg.test(loginUser.username)) {
    usernameMsg.value = '';
    return true;
  } else {
    usernameMsg.value = '用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位';
    return false;
  }
};
function checkPassword(){
  // 用正则表达式验证密码
  const reg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
  if (reg.test(loginUser.user_pwd)) {
    userPwdMsg.value = '';
    return true;
  } else {
    userPwdMsg.value = '// 密码至少包含字母、数字、特殊字符中的两种,长度至少6位';
    return false;
  }
};
async function login() {
  var flag = checkUsername() && checkPassword();
  if (!flag) {
    return;
  }
  let {data} = await request.post('TodoList/user/login',loginUser);
  if (data.code === 200) {
    console.log(data);
    sysUser.setUid(data.data.loginUser.uid);
    sysUser.setUsername(data.data.loginUser.username);
    router.push('/todolist');
  } else if (data.code === 503) {
    alert("密码错误");
  } else if (data.code === 502) {
    alert("用户名不存在");  
  }
  else {
    alert("未知错误,登录失败");
  }
};
  

</script>

<template>
  <div class="login-wrapper">
    <div class="login-card">
      <div class="login-header">
        <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-square"><polyline points="9 11 12 14 22 4"></polyline><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>
        <h2>用户登录</h2>
        <p>欢迎回来!请登录您的账户。</p>
      </div>
      <form @submit.prevent="handleLogin" class="login-form">
        <div class="input-group">
          <label for="username">用户名</label>
          <input
            type="text"
            id="username"
            v-model="loginUser.username"
            @blur="checkUsername()"
            placeholder="请输入用户名"
            required
          />
          <div v-if="usernameMsg" class="error-msg">{{ usernameMsg }} </div>
        </div>
        <div class="input-group">
          <label for="password">密码</label>
          <input
            type="password"
            id="password"
            v-model="loginUser.user_pwd"
            @blur = "checkPassword()"
            placeholder="请输入密码"
            required
          />
          <div v-if="userPwdMsg" class="error-msg">{{ userPwdMsg }} </div>
        </div>
        <button type="submit" class="btn-login" @click="login()">登 录</button>
      </form>
      <div class="login-footer">
        <router-link to="/regist">还没有账号?立即注册</router-link>
      </div>
    </div>
  </div>
</template>

<style scoped>
.error-msg {
  color: red;
  font-size: 0.8rem;
  margin-top: 0.2rem;
  }
.login-wrapper {
  /* min-height: 100vh; */ /* 移除 min-height */
  flex-grow: 1; /* 让 wrapper 在 main-content 内填充可用空间 */
  display: flex;
  justify-content: center;
  align-items: center;
  /* background-color: #fff; */ /* 背景色由 main-content 或 body 控制,或者按需保留 */
  padding: 2rem; /* 保留内边距 */
}

.login-card {
  background: white;
  padding: 2.5rem 3rem;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  width: 100%;
  max-width: 420px;
  text-align: center;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.login-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}


.login-header {
  margin-bottom: 2rem;
}

.login-header svg {
  color: #4361ee;
  margin-bottom: 0.5rem;
}

.login-header h2 {
  font-size: 1.8rem;
  color: #333;
  margin-bottom: 0.5rem;
}

.login-header p {
  color: #777;
  font-size: 0.95rem;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 1.5rem; /* 增加输入框间距 */
}

.input-group {
  text-align: left;
}

.input-group label {
  display: block;
  margin-bottom: 0.5rem;
  color: #555;
  font-weight: 500;
  font-size: 0.9rem;
}

.input-group input {
  width: 100%;
  padding: 0.8rem 1rem; /* 增加内边距 */
  border: 1px solid #ddd;
  border-radius: 6px; /* 更圆润的边角 */
  font-size: 1rem;
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
  box-sizing: border-box; /* 确保 padding 不会撑大元素 */
}

.input-group input:focus {
  outline: none;
  border-color: #4361ee;
  box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1); /* 添加聚焦光晕效果 */
}

.btn-login {
  padding: 0.9rem; /* 增加按钮内边距 */
  background: linear-gradient(90deg, #4361ee 0%, #3a53c0 100%); /* 渐变背景 */
  color: white;
  border: none;
  border-radius: 6px;
  font-size: 1.1rem; /* 增大字体 */
  font-weight: 600; /* 加粗字体 */
  cursor: pointer;
  transition: all 0.3s ease;
  margin-top: 1rem; /* 与上方输入框的间距 */
  letter-spacing: 1px; /* 增加字间距 */
}

.btn-login:hover {
  background: linear-gradient(90deg, #3a53c0 0%, #4361ee 100%);
  box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
  transform: translateY(-2px);
}

.login-footer {
  margin-top: 1.5rem;
  font-size: 0.9rem;
}

.login-footer a {
  color: #4361ee;
  text-decoration: none;
  transition: color 0.3s ease;
}

.login-footer a:hover {
  color: #3451db;
  text-decoration: underline;
}
</style>

9.启动Tomcat服务器,在前端页面中去登录,可以发现网页右上角出现了用户名和退出登录按钮

点击退出登录,可以看到网页右上角又变成了登录和注册按钮,且页面跳转到了登录页

未登录限制访问

1.未登陆时在浏览器访问代表事项,发现可以直接访问,我们要把它改成只有登录之后才能访问

2.找到router.js文件,把代码改成下面这样

import { createRouter, createWebHashHistory } from 'vue-router' // 从vue-router中导入createRouter和createWebHashHistory函数
import Header from '../components/Header.vue' // 导入Header组件
import Login from '../components/Login.vue' // 导入Login组件
import Regist from '../components/Regist.vue' // 导入Regist组件
import ShowTodoList from '../components/ShowTodoList.vue' // 导入ShowTodoList组件
import pinia from '../pinia.js' // 导入pinia实例
import {defineUser } from '../store/userStore.js' // 导入用户状态管理模块
let sysUser = defineUser(pinia) // 实例化用户状态管理模块


// 创建路由实例
const router = createRouter({
    history: createWebHashHistory(), // 使用hash模式的路由历史记录管理
    routes: [ // 定义路由配置
        { // 首页路由配置
            path: '/', // 路径为根路径
            redirect: '/todolist', // 路由名称为'Header'
        },
        { // 登录页面路由配置
            path: '/login', // 路径为/login
            name: 'Login', // 路由名称为'Login'
            component: Login // 对应的组件为Login
        },
        { // 注册页面路由配置
            path: '/regist', // 路径为/regist
            name: 'Regist', // 路由名称为'Regist'
            component: Regist // 对应的组件为Regist
        },
        { // 显示待办事项列表页面路由配置
            path: '/todolist', // 路径为/todolist
            name: 'ShowTodoList', // 路由名称为'ShowTodoList'
            component: ShowTodoList // 对应的组件为ShowTodoList
        },
    ]
})



// 路由 的全局前置守卫 判断是否可以访问todolist页面
router.beforeEach((to, from, next) => {
    if (to.path == '/todolist') {
        // 登陆过放行
        // 没登录 回到登录页
        if (sysUser.username == null) {
            next("/login")
        } else {
            next()
        }
    } else {
        next()
    }
})

export default router // 导出路由实例

3.再次在未登录的时候尝试访问待办事项页,可以发现网页强行跳转到了登录页。

若登录之后,则可以正常访问


评论 (0)

暂无评论,来发表第一条评论吧!