news 2026/4/15 11:48:15

SchoolDash Alpha冲刺随笔2 - Day 3

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SchoolDash Alpha冲刺随笔2 - Day 3

SchoolDash Alpha冲刺随笔2 - Day 3
课程与作业信息

所属课程:软件工程实践
作业要求来源:第五次作业——Alpha冲刺
本篇目标:记录冲刺第3天进度,展示燃尽图、运行效果、成员工作成果

  1. 项目燃尽图(Burn-up Chart)

    已完成需求的75%

  2. 本日冲刺整体进展

完成用户注册、登录功能,后端JWT验证,前端多角色Token存储。
解决角色权限问题:实现不同角色(用户、骑手、管理员)独立Token管理。

  1. 项目最新运行效果

用户端注册界面

用户端登录界面
管理员端登录界面

骑手端登录界面

  1. 今日工作成果

(后端开发)
认证路由与JWT实现

const express = require('express'); const router = express.Router(); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); // 注册接口(前端请求的 /api/auth/register 对应这里) router.post('/register', async (req, res) => { try { const { username, password, phone } = req.body; // 校验参数 if (!username || !password) { return res.status(400).json({ code: 400, msg: '用户名和密码不能为空' }); } // 检查用户名是否重复 const existingUser = await User.findOne({ where: { username } }); if (existingUser) { return res.status(400).json({ code: 400, msg: '用户名已存在' }); } // 创建用户(密码由User模型自动加密) const user = await User.create({ username, password: password, // 传递明文密码,模型会自动加密 phone: phone || '', role: 'user' // 默认注册为普通用户 }); // 返回成功结果 res.status(201).json({ code: 200, msg: '注册成功', data: { id: user.id, username: user.username, role: user.role } }); } catch (error) { console.error('注册接口报错:', error); res.status(500).json({ code: 500, msg: '服务器错误,注册失败' }); } }); // 登录接口(配套前端登录功能) router.post('/login', async (req, res) => { try { const { username, password } = req.body; // 查找用户 const user = await User.findOne({ where: { username } }); if (!user) { return res.status(400).json({ code: 400, msg: '用户名不存在,请检查输入或前往注册' }); } // 验证密码 const isPasswordValid = bcrypt.compareSync(password, user.password); if (!isPasswordValid) { return res.status(400).json({ code: 400, msg: '密码不正确,请重新输入' }); } // 生成 JWT Token const token = jwt.sign( { id: user.id, role: user.role }, process.env.JWT_SECRET || 'school_dash_jwt_secret_2025', { expiresIn: '24h' } ); // 返回登录结果 res.json({ code: 200, msg: '登录成功', data: { token, username: user.username, role: user.role } }); } catch (error) { console.error('登录接口报错:', error); res.status(500).json({ code: 500, msg: '服务器繁忙,请稍后再试' }); } }); // 管理员登录接口 router.post('/admin/login', async (req, res) => { try { const { username, password } = req.body; const user = await User.findOne({ where: { username } }); if (!user) { return res.status(400).json({ code: 400, msg: '管理员账号不存在,请联系系统管理员' }); } if (user.role !== 'admin') { return res.status(403).json({ code: 403, msg: '该账号没有管理员权限,请使用管理员账号登录' }); } const isPasswordValid = bcrypt.compareSync(password, user.password); if (!isPasswordValid) { return res.status(400).json({ code: 400, msg: '管理员密码不正确,请重新输入' }); } const token = jwt.sign( { id: user.id, role: user.role }, process.env.JWT_SECRET || 'school_dash_jwt_secret_2025', { expiresIn: '24h' } ); res.json({ code: 200, msg: '管理员登录成功', data: { token, username: user.username, role: user.role } }); } catch (error) { console.error('管理员登录接口报错:', error); res.status(500).json({ code: 500, msg: '服务器繁忙,请稍后再试' }); } }); // 骑手登录接口 router.post('/rider/login', async (req, res) => { try { const { username, password } = req.body; const user = await User.findOne({ where: { username } }); if (!user) { return res.status(400).json({ code: 400, msg: '骑手账号不存在,请联系管理员开通账号' }); } if (user.role !== 'rider') { return res.status(403).json({ code: 403, msg: '该账号没有骑手权限,请使用骑手账号登录' }); } const isPasswordValid = bcrypt.compareSync(password, user.password); if (!isPasswordValid) { return res.status(400).json({ code: 400, msg: '骑手密码不正确,请重新输入' }); } const token = jwt.sign( { id: user.id, role: user.role }, process.env.JWT_SECRET || 'school_dash_jwt_secret_2025', { expiresIn: '24h' } ); res.json({ code: 200, msg: '骑手登录成功', data: { token, username: user.username, role: user.role } }); } catch (error) { console.error('骑手登录接口报错:', error); res.status(500).json({ code: 500, msg: '服务器繁忙,请稍后再试' }); } }); module.exports = router;

这是基于 Express 的用户认证路由,提供普通用户注册(/register)及普通用户、管理员、骑手三类专属登录接口,通过 bcrypt 加密校验密码,JWT 生成 24 小时身份令牌,同时校验角色权限并处理异常,接口路径统一挂载在 /api/auth/ 下。

(前端开发)

用户登录界面

<template> <div class="login-page"> <div class="back-button" @click="$router.push('/')"> <font-awesome-icon icon="fa-solid fa-arrow-left" class="back-icon" /> </div> <div class="login-container"> <!-- Logo区域 --> <div class="school-dash-logo"> <font-awesome-icon icon="fa-solid fa-graduation-cap" class="logo-icon" /> <div class="logo-text">School Dash</div> </div> <!-- 标题 --> <div class="login-title">用户登录</div> <!-- 用户名输入框 --> <div class="form-item"> <label class="form-label">用户名</label> <input type="text" class="form-input" v-model="username" placeholder="请输入用户名" @keyup.enter="handleLogin" /> </div> <!-- 密码输入框 --> <div class="form-item"> <label class="form-label">密码</label> <input type="password" class="form-input" v-model="password" placeholder="请输入密码" @keyup.enter="handleLogin" /> </div> <!-- 登录按钮 --> <button class="login-btn" @click="handleLogin">登录</button> <!-- 注册链接 --> <div class="register-link"> 还没有账号?<span @click="$router.push('/user/register')">立即注册</span> </div> </div> </div> </template> <script setup> import { ref } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { faGraduationCap, faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import request from '../../utils/request'; // 路由实例 const router = useRouter(); // 表单数据绑定 const username = ref(''); const password = ref(''); // 登录核心逻辑 const handleLogin = async () => { if (!username.value) return ElMessage.warning('请输入用户名'); if (!password.value) return ElMessage.warning('请输入密码'); try { const res = await request({ url: '/auth/login', method: 'POST', data: { username: username.value.trim(), password: password.value } }); if (!res) { return ElMessage.error('登录失败,服务器无响应'); } if (res.code === 200) { ElMessage.success('登录成功!'); // 关键1:存储后端返回的真实token和用户信息 localStorage.setItem('token', res.data.token); localStorage.setItem('role', res.data.role); localStorage.setItem('username', res.data.username); // 关键2:跳转到用户首页 router.push('/user'); } else { ElMessage.error(res.msg || '登录失败,请检查账号密码'); } } catch (error) { console.error('登录请求异常:', error); const errMsg = error.message || '网络异常,请稍后重试'; ElMessage.error(errMsg); } }; </script> <style scoped> /* 页面整体布局 */ .login-page { min-height: 100vh; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; position: relative; } /* 返回按钮样式 */ .back-button { position: absolute; top: 20px; left: 20px; width: 40px; height: 40px; background-color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); cursor: pointer; transition: all 0.2s ease; z-index: 10; } .back-button:hover { background-color: #f0f0f0; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .back-icon { font-size: 18px; color: #4299e1; } /* 登录容器 */ .login-container { width: 100%; max-width: 350px; background-color: #ffffff; padding: 32px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); box-sizing: border-box; } /* Logo样式 */ .school-dash-logo { text-align: center; margin-bottom: 24px; } .logo-icon { font-size: 48px; color: #4299e1; margin-bottom: 12px; } .logo-text { font-size: 24px; font-weight: 600; color: #333333; } /* 标题样式 */ .login-title { font-size: 20px; font-weight: 600; color: #333333; margin-bottom: 24px; text-align: center; } /* 表单项目样式 */ .form-item { margin-bottom: 20px; } .form-label { display: block; font-size: 14px; color: #333333; margin-bottom: 8px; text-align: left; } /* 输入框样式 */ .form-input { width: 100%; padding: 12px 16px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 14px; outline: none; box-sizing: border-box; transition: all 0.2s ease; } .form-input:focus { border-color: #4299e1; box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.1); } .form-input::placeholder { color: #999999; } /* 登录按钮样式 */ .login-btn { width: 100%; padding: 14px; background-color: #4299e1; color: #ffffff; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.2s ease; margin-bottom: 16px; } .login-btn:hover { background-color: #3a86cf; } .login-btn:active { background-color: #3182ce; } .login-btn:disabled { background-color: #a7c0ff; cursor: not-allowed; } /* 注册链接样式 */ .register-link { text-align: center; font-size: 14px; color: #666666; } .register-link span { color: #4299e1; cursor: pointer; margin-left: 4px; } .register-link span:hover { text-decoration: underline; } </style>

管理员登陆界面

<template> <div class="admin-login-page"> <div class="back-button" @click="$router.push('/')"> <font-awesome-icon icon="fa-solid fa-arrow-left" class="back-icon" /> </div> <div class="login-container"> <div class="school-dash-logo"> <font-awesome-icon icon="fa-solid fa-graduation-cap" class="logo-icon" /> <div class="logo-text">School Dash 管理后台</div> </div> <div class="login-title">管理员登录</div> <div class="form-item"> <label class="form-label">用户名</label> <input type="text" class="form-input" v-model="username" placeholder="请输入管理员用户名" @keyup.enter="handleLogin" /> </div> <div class="form-item"> <label class="form-label">密码</label> <input type="password" class="form-input" v-model="password" placeholder="请输入管理员密码" @keyup.enter="handleLogin" /> </div> <button class="login-btn" @click="handleLogin">登录</button> </div> </div> </template> <script setup> import { ref } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { faGraduationCap, faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import request from '../../utils/request'; const router = useRouter(); const username = ref(''); const password = ref(''); const handleLogin = async () => { // 表单校验 if (!username.value.trim()) return ElMessage.warning('请输入用户名'); if (!password.value.trim()) return ElMessage.warning('请输入密码'); try { const res = await request({ url: '/auth/admin/login', method: 'POST', data: { username: username.value.trim(), password: password.value.trim() } }); if (res.code === 200) { // 存储管理员token和名称 localStorage.setItem('adminToken', res.data.token); // 后端返回的是 data.username(非 data.user.username) localStorage.setItem('adminName', res.data.username); ElMessage.success('登录成功'); router.push('/admin/dashboard'); } else { ElMessage.error(res.msg); } } catch (error) { ElMessage.error('登录失败,请检查后端服务'); console.error('登录报错:', error); } }; </script> <style scoped> .admin-login-page { min-height: 100vh; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; position: relative; } /* 返回按钮样式 */ .back-button { position: absolute; top: 20px; left: 20px; width: 40px; height: 40px; background-color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); cursor: pointer; transition: all 0.2s ease; z-index: 10; } .back-button:hover { background-color: #f0f0f0; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .back-icon { font-size: 18px; color: #4299e1; } .login-container { width: 100%; max-width: 350px; background-color: #ffffff; padding: 32px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); box-sizing: border-box; } .school-dash-logo { text-align: center; margin-bottom: 24px; } .logo-icon { font-size: 48px; color: #4299e1; margin-bottom: 12px; } .logo-text { font-size: 24px; font-weight: 600; color: #333; } .login-title { font-size: 20px; font-weight: 600; color: #333; margin-bottom: 24px; text-align: center; } .form-item { margin-bottom: 20px; } .form-label { display: block; font-size: 14px; color: #333; margin-bottom: 8px; text-align: left; } .form-input { width: 100%; padding: 12px 16px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 14px; outline: none; box-sizing: border-box; transition: all 0.2s ease; } .form-input:focus { border-color: #4299e1; box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.1); } .login-btn { width: 100%; padding: 14px; background-color: #4299e1; color: #ffffff; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.2s ease; margin-bottom: 16px; } .login-btn:hover { background-color: #3a86cf; } </style>

骑手登陆界面

<template> <div class="rider-login-page"> <div class="back-button" @click="$router.push('/')"> <font-awesome-icon icon="fa-solid fa-arrow-left" class="back-icon" /> </div> <div class="login-container"> <div class="school-dash-logo"> <font-awesome-icon icon="fa-solid fa-motorcycle" class="logo-icon" /> <div class="logo-text">School Dash 骑手端</div> </div> <div class="login-title">骑手登录</div> <div class="form-item"> <label class="form-label">用户名</label> <input type="text" class="form-input" v-model="username" placeholder="请输入用户名" @keyup.enter="handleLogin" /> </div> <div class="form-item"> <label class="form-label">密码</label> <input type="password" class="form-input" v-model="password" placeholder="请输入密码" @keyup.enter="handleLogin" /> </div> <button class="login-btn" @click="handleLogin">登录</button> </div> </div> </template> <script setup> import { ref } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { faMotorcycle, faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import request from '../../utils/request'; const router = useRouter(); const username = ref(''); const password = ref(''); const handleLogin = async () => { // 表单校验 if (!username.value.trim()) return ElMessage.warning('请输入用户名'); if (!password.value.trim()) return ElMessage.warning('请输入密码'); try { const res = await request({ url: '/auth/rider/login', method: 'POST', data: { username: username.value.trim(), password: password.value.trim() } }); if (res.code === 200) { // 存储骑手token和名称 localStorage.setItem('riderToken', res.data.token); // 后端返回的是 data.username localStorage.setItem('riderName', res.data.username); ElMessage.success('登录成功'); router.push('/rider/dashboard'); } else { ElMessage.error(res.msg); } } catch (error) { ElMessage.error('登录失败,请检查后端服务'); console.error('登录报错:', error); } }; </script> <style scoped> .rider-login-page { min-height: 100vh; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; position: relative; } /* 返回按钮样式 */ .back-button { position: absolute; top: 20px; left: 20px; width: 40px; height: 40px; background-color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); cursor: pointer; transition: all 0.2s ease; z-index: 10; } .back-button:hover { background-color: #f0f0f0; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .back-icon { font-size: 18px; color: #4299e1; } .login-container { width: 100%; max-width: 350px; background-color: #ffffff; padding: 32px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); box-sizing: border-box; } .school-dash-logo { text-align: center; margin-bottom: 24px; } .logo-icon { font-size: 48px; color: #4299e1; margin-bottom: 12px; } .logo-text { font-size: 24px; font-weight: 600; color: #333; } .login-title { font-size: 20px; font-weight: 600; color: #333; margin-bottom: 24px; text-align: center; } .form-item { margin-bottom: 20px; } .form-label { display: block; font-size: 14px; color: #333; margin-bottom: 8px; text-align: left; } .form-input { width: 100%; padding: 12px 16px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 14px; outline: none; box-sizing: border-box; transition: all 0.2s ease; } .form-input:focus { border-color: #4299e1; box-shadow: 0 0 0 2px rgba(66, 153, 225, 0.1); } .login-btn { width: 100%; padding: 14px; background-color: #4299e1; color: #ffffff; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.2s ease; margin-bottom: 16px; } .login-btn:hover { background-color: #3a86cf; } </style>

(测试)

任务:登录功能测试
成果:完成测试(正常登录、错误密码、角色切换),无bug

  1. 本日小结与明日计划

今日总结:认证模块稳定,支持多角色。
存在问题:跨域配置需完善
明日计划:商品列表与分类模块

进度稳健,继续冲!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 11:46:56

别熬论文了!paperzz AI 让 “初稿难产” 变成 “一杯咖啡的事”

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿 paperzz - 毕业论文-AIGC论文检测-AI智能降重-ai智能写作https://www.paperzz.cc/dissertation 毕业季的论文有多磨人&#xff1f;打开文档盯着空白页半小时&#xff0c;选题改了 8 版还被导师批 “太泛”…

作者头像 李华
网站建设 2026/4/15 11:48:33

2、网络基础与Linux网络应用全解析

网络基础与Linux网络应用全解析 1. 网络的起源与定义 网络的概念几乎和电信本身一样古老。想象一下石器时代,人们可能用鼓在个体之间传递信息。比如,洞穴人A想邀请洞穴人B一起玩互相扔石头的游戏,但他们住得太远,B听不到A敲鼓的声音。A有几个选择:一是走到B的住处;二是…

作者头像 李华
网站建设 2026/4/12 0:40:59

6、Linux TCP/IP 网络配置全攻略

Linux TCP/IP 网络配置全攻略 在 Linux 系统中配置 TCP/IP 网络是一项基础且重要的工作,它涉及多个步骤和工具的使用。下面将详细介绍如何在 Linux 系统上完成 TCP/IP 网络的配置。 1. 挂载 /proc 文件系统 部分 Linux NET - 2 和 NET - 3 版本的配置工具依赖 /proc 文件系…

作者头像 李华
网站建设 2026/4/13 7:23:28

27、量子计算算法:原理与应用

量子计算算法:原理与应用 1. Deutsch - Jozsa 算法 Deutsch - Jozsa 算法由 David Deutsch 和 Richard Jozsa 在 1992 年发明,旨在区分两种类型的函数:常数函数(所有输入对应相同输出)和平衡函数(输出中 0 和 1 的数量相等)。 经典算法至少需要两次查询才能确定函数类…

作者头像 李华
网站建设 2026/4/10 13:28:27

2、量子计算:解锁未来计算的新力量

量子计算:解锁未来计算的新力量 1. 量子计算的独特魅力与强大潜力 量子计算,这个听起来充满神秘色彩的领域,正逐渐走进我们的视野。它与我们日常使用的经典计算有着本质的区别,却又能与之相辅相成,为解决人类面临的诸多复杂问题提供了全新的途径。 1.1 量子计算为何如此…

作者头像 李华
网站建设 2026/4/11 2:19:05

GSE宏编译器:革命性自动化战斗系统完全指南

GSE宏编译器&#xff1a;革命性自动化战斗系统完全指南 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Curse p…

作者头像 李华