零基础快速开发Vue图书管理系统—登录注册篇(一)
一、图书管理系统项目功能
二、项目技术选型
- 前端主要采用:
Vue3.x (vuex/vue-router)、Ant Design Vue、Axios
等 - 服务端主要采用:
Node.js、Koa、Mongoose
等 - 数据库主要采用:
MongoDB
三、使用vue-cli3创建项目
四、搭建所需的文件
在view的目录下新建Auth文件夹,里面分别放以下三个文件
前端UI框架主要采用Ant Design Vue
😛index.vue内容如下
<template> <div class="auth"> <div class="bg"></div> <div class="title-info"> <img src="https://ncstatic.clewm.net/rsrc/2020/1016/02/4757e4910cb527fc040d019a93ded74f.png?x-oss-process=image/resize,w_750/format,gif/sharpen,100/quality,Q_80/interlace,1/auto-orient,1" alt=""> <h2 class="title">图书后台管理系统</h2> </div> <div class="form"> <a-tabs> <a-tab-pane key="1" tab="登录"> <div class="item"> <a-input size="large" placeholder="账户"> <template v-slot:prefix> <UserOutlined /> </template> </a-input> </div> <div class="item"> <a-input size="large" placeholder="密码"> <template v-slot:prefix> <LockOutlined /> </template> </a-input> </div> <div class="item"> <a href="">忘记密码</a> </div> <div class="item"> <a-button size="large" type="primary"> 登录 </a-button> </div> </a-tab-pane> <a-tab-pane key="2" tab="注册"> <div class="item"> <a-input size="large" placeholder="账户"> <template v-slot:prefix> <UserOutlined /> </template> </a-input> </div> <div class="item"> <a-input size="large" placeholder="密码"> <template v-slot:prefix> <LockOutlined /> </template> </a-input> </div> <div class="item"> <a-input size="large" placeholder="邀请码"> <template v-slot:prefix> <MailOutlined /> </template> </a-input> </div> <div class="item"> <a-button size="large" type="primary"> 注册 </a-button> </div> </a-tab-pane> </a-tabs> </div> </div> </template> <script src="./index.js"> </script> <style lang="scss" scoped> @import './index.scss' </style>
😅index.scss内容如下:
.bg { position: fixed; left: 0; top: 0; right: 0; bottom: 0; background-image: url("https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg"); background-repeat: no-repeat; background-size: cover; background-position: center center; } .auth { .title-info { display: flex; margin-top: 100px; text-align: center; align-items: center; justify-content: center; margin-bottom: 32px; img { width: 60px; height: 60px; } h2 { margin: 0; margin-left: 18px; } } .form { width: 400px; margin: 0 auto; .item { margin-bottom: 16px; button { width: 100%; } } } }
🤣index.js内容如下:
import { defineComponent } from 'vue'; import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons-vue'; export default defineComponent({ components: { UserOutlined, LockOutlined, MailOutlined }, setup() { } });
五、服务端开发
执行以下命令安装koa
包
npm i @koa/router
服务端文件结构如下:
六、nodemon使用
编写调试Node的时候,项目代码做了修改,需要频繁手动停止,在重新启动,非常繁琐,使用nodemon能够监听项目文件的变动,当代码被修改后,nodemon会自动重启项目,极大方便了开发和调试
在终端中,运行如下命令,即可将nodemon安装为全局可用的工具:
npm install -g nodemon
- 传统的方式是运行node app.js命令启动项目,需要手动重启
- 现在将node命令替换为nodemon命令,使用
nodemon app.js
启动项目,会自动重启
七、使用JWT和Session实现登录注册
📢📢📢登录部分(服务端)
核心代码如下:
const Router = require('@koa/router'); const mongoose = require('mongoose'); const { getBody } = require('../../helpers/utils') const jwt = require('jsonwebtoken'); const User = mongoose.model('User'); const router = new Router({ prefix: '/auth', }); router.post('/register', async(ctx) => { // console.log(ctx.request.body); const { account, password, } = getBody(ctx); const one = await User.findOne({ account, }).exec(); if (one) { ctx.body = { code: 0, msg: '已存在该用户', data: null, } return; } const user = new User({ account, password }); const res = await user.save(); ctx.body = { code: 1, msg: '注册成功', data: res, } }); router.post('/login', async(ctx) => { const { account, password, } = getBody(ctx); const one = await User.findOne({ account, }).exec(); if (!one) { ctx.body = { code: 0, msg: '用户名或者密码错误', data: null, } return; } const user = { account: one.account, _id: one._id, } if (one.password === password) { ctx.body = { code: 1, msg: '登录成功', data: { user, token: jwt.sign(user, 'manage') }, } return; } ctx.body = { code: 0, msg: '用户名或密码错误', data: null, } }); module.exports = router;
📢📢📢登录部分(前端)
核心代码:
import { defineComponent, reactive } from 'vue'; import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons-vue'; import { auth } from '@/service'; export default defineComponent({ components: { UserOutlined, LockOutlined, MailOutlined, }, setup() { //注册相关的逻辑 const regForm = reactive({ account: '', password: '', }) //注册逻辑 const register = () => { auth.register(regForm.account, regForm.password) } //登录相关的逻辑 const loginForm = reactive({ account: '', password: '', }); //登录逻辑 const login = () => { auth.login(loginForm.account, loginForm.password) } return { //注册相关的数据 register, regForm, //登录相关的数据 login, loginForm, } } });
index.vue
<template> <div class="auth"> <div class="bg"></div> <div class="title-info"> <img src="https://ncstatic.clewm.net/rsrc/2020/1016/02/4757e4910cb527fc040d019a93ded74f.png?x-oss-process=image/resize,w_750/format,gif/sharpen,100/quality,Q_80/interlace,1/auto-orient,1" alt=""> <h2 class="title">图书后台管理系统</h2> </div> <div class="form"> <a-tabs> <a-tab-pane key="1" tab="登录"> <div class="item"> <a-input v-model:value="loginForm.account" size="large" placeholder="账户"> <template v-slot:prefix> <UserOutlined /> </template> </a-input> </div> <div class="item"> <a-input v-model:value="loginForm.password" size="large" placeholder="密码"> <template v-slot:prefix> <LockOutlined /> </template> </a-input> </div> <div class="item"> <a href="">忘记密码</a> </div> <div class="item"> <a-button @click="login" size="large" type="primary"> 登录 </a-button> </div> </a-tab-pane> <a-tab-pane key="2" tab="注册"> <div class="item"> <a-input size="large" placeholder="账户" v-model:value="regForm.account" > <template v-slot:prefix> <UserOutlined /> </template> </a-input> </div> <div class="item"> <a-input size="large" placeholder="密码" v-model:value="regForm.password" > <template v-slot:prefix> <LockOutlined /> </template> </a-input> </div> <div class="item"> <a-input size="large" placeholder="邀请码"> <template v-slot:prefix> <MailOutlined /> </template> </a-input> </div> <div class="item"> <a-button size="large" type="primary" @click="register"> 注册 </a-button> </div> </a-tab-pane> </a-tabs> </div> </div> </template> <script src="./index.js"> </script> <style lang="scss" scoped> @import './index.scss' </style>
八、交互优化、表单校验、处理请求结果优化
登录注册逻辑校验
九、邀请码实现,完善注册流程
const Router = require('@koa/router'); const mongoose = require('mongoose'); const { getBody } = require('../../helpers/utils') const jwt = require('jsonwebtoken'); const User = mongoose.model('User'); const InviteCode = mongoose.model('InviteCode'); const router = new Router({ prefix: '/auth', }); router.post('/register', async(ctx) => { const { account, password, inviteCode, } = getBody(ctx); //表单校验 if (account === '' || password === '' || inviteCode === '') { ctx.body = { code: 0, msg: '字段不能为空', data: null, } return; } //找是否有邀请码 const findCode = await InviteCode.findOne({ code: inviteCode, }).exec(); //如果没有找到邀请码 if ((!findCode) || findCode.user) { ctx.body = { code: 0, msg: '邀请码不正确', data: null, } return; } //去找account为传递上来的account的用户 const findUser = await User.findOne({ account, }).exec(); //判断是否有用户 if (findUser) { //如果有表示已经存在 ctx.body = { code: 0, msg: '已存在该用户', data: null, } return; } //创建用户 const user = new User({ account, password }); //把创建的用户同步到mongdb const res = await user.save(); findCode.user = res._id; findCode.meta.updatedAt = new Date().getTime(); await findCode.save(); //响应成功 ctx.body = { code: 1, msg: '注册成功', data: res, } }); router.post('/login', async(ctx) => { const { account, password, } = getBody(ctx); if (account === '' || password === '') { ctx.body = { code: 0, msg: '字段不能为空', data: null, } return; } const one = await User.findOne({ account, }).exec(); if (!one) { ctx.body = { code: 0, msg: '用户名或者密码错误', data: null, } return; } const user = { account: one.account, _id: one._id, } if (one.password === password) { ctx.body = { code: 1, msg: '登录成功', data: { user, token: jwt.sign(user, 'manage') }, } return; } ctx.body = { code: 0, msg: '用户名或密码错误', data: null, } }); module.exports = router;