目录
1.1 后端验证接口:使用 express+mongodb 模拟
2.1 使用ant-design-vue搭建一个简易的登录页面
1. 准备工作
1.1 后端验证接口:使用 express+mongodb 模拟
// app.js
const express = require('express');
const { User } = require('./mongoose');
const app = express();
const port = 3000;
app.use(express.json())
// post请求
// 注册
app.post('/register', async(req, res)=>{
const user = await User.create({
username: req.body.username,
password: req.body.password,
})
res.send(user)
})
// 登录
app.post('/login', async(req, res)=>{
let user = await User.findOne({
username: req.body.username
});
if(!user){
return res.status(401).send({
message: '用户不存在'
})
};
const isValidPassword = await require('bcryptjs').compare(
req.body.password,
user.password
);
if(!isValidPassword){
return res.status(401).send({
message: '密码错误'
})
};
const jwt = require('jsonwebtoken');
const token = jwt.sign({
id: String(user.id)
}, 'express-auth')
res.send({
user, token
})
})
app.listen(port, ()=>{
console.log(`端口${port}监听中`);
})
// mongoose.js
const mongoose = require('mongoose');
const bcryptjs = require('bcryptjs')
mongoose.connect('mongodb://localhost:27017/express-auth', {
useNewUrlParser: true
})
const connect = mongoose.connection;
connect.on('connected', ()=>{
console.log('数据库连接成功');
})
const User = mongoose.model('User', new mongoose.Schema({
username: {type: String, unique: true},
password:{type: String, set(val){
return bcryptjs.hashSync(val, 10)
}}
}))
//User.db.dropCollection('users')
module.exports = { User }
1.2 注册两个用户
管理员:
username: admin
password: 123
role: ['admin']
超级管理员
username: superadmin
password: 123
role: ['superadmin']
2. Login页面
2.1 使用ant-design-vue搭建一个简易的登录页面
2.2 给按钮绑定点击事件
表单验证+跳转
<template lang="">
<div class="wrapper">
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<HelloWorld msg="You did it!" />
</div>
<a-form style="text-align: center">
<a-form-item label="Username" required v-bind="validateInfos.username">
<a-input v-model:value="userInfo.username"/>
</a-form-item>
<a-form-item label="Password" required v-bind="validateInfos.password">
<a-input v-model:value="userInfo.password" />
</a-form-item>
<a-form-item>
<a-button @click.prevent="handleLogin">登录</a-button>
</a-form-item>
</a-form>
</template>
<script setup>
import HelloWorld from '@/components/HelloWorld.vue'
import { reactive, toRaw } from 'vue'
import { useUserStore } from '@/stores/modules/user'
import { Form } from 'ant-design-vue'
const userStore = useUserStore()
const userInfo = reactive({
username: '',
password: ''
})
const useForm = Form.useForm
const { validate, validateInfos } = useForm(
userInfo,
reactive({
username: [{ required: true, message: '请输入用户名' }],
password: [{ required: true, message: '请输入密码' }]
})
)
function handleLogin() {
// 表单验证,成功后跳转
validate()
.then(async () => {
const data = toRaw(userInfo)
if (data) {
await userStore.login(data)
}
})
.catch(err=>{
console.log(err)
})
}
</script>
3. 表单验证成功后:发送登录请求
注:将用户相关操作写在src/store/userStore中
发请求(登录接口)验证用户名及密码是否正确。错误时会出现相应弹窗。
注:请求使用axios进行二次封装,具体见博客vue + vite + js 项目中 axios的二次封装_sunnakay的博客-CSDN博客,其中错误弹窗在响应拦截器中设置。
4. 登录成功后操作
①保存token到store中,同时保存到客户端的sessionStorage中以保持登录效果。(这里token的保存也有很多值得学习的,大家可以自己上网搜索相关内容)
②获取并保存用户详细信息:可能包括用户id、用户名、用户角色等内容。(注:用户角色涉及到权限管理的内容)
在这里同样使用express+mongodb模拟接口并返回数据,在src/api/user.js中定义发送请求的api,然后在当前文件src/stores/modules/user.js中调用
③动态添加路由表。详情可见另一篇博客:vue + vite + js 后台管理系统:权限控制+路由/菜单动态加载_sunnakay的博客-CSDN博客
④跳转后台主页。
整体代码如下:
import { defineStore } from 'pinia'
import { loginApi, getUserInfo } from '@/api/user'
import { usePermissonStore } from './permission'
import router from '@/router'
import { isArray } from '@/utils/is'
export const useUserStore = defineStore('users', {
state: () => {
return {
userInfo: {},
token: undefined,
roleList: []
}
},
getters: {
getUserInfo(state){
return state.userInfo || {};
},
getToken(state) {
return state.token || undefined
}
},
actions: {
setToken(info) {
this.token = info ? info : ''
window.sessionStorage.setItem('token',info)
},
setRoleList(info) {
this.roleList = info
window.sessionStorage.setItem('roleList',info)
},
setUserInfo(info) {
this.userInfo = info
window.sessionStorage.setItem('userInfo',info)
},
async login(params) {
try {
// 发请求
const result = await loginApi(params)
// 保存token
const { token, username } = result
this.setToken(token)
// 进行登录成功后跳转等工作
return this.afterLoginAction(username)
} catch (error) {
return Promise.reject(error)
}
},
async afterLoginAction() {
// 获取用户信息
const userInfo = await this.getUserInfoAction();
// 动态匹配路由,加载菜单
const permissionStore = usePermissonStore()
const routes = await permissionStore.buildRoutesAction()
routes.forEach((route) => {
router.addRoute(route)
})
// 跳转主页面
router.push('/dashboard')
return userInfo
},
async getUserInfoAction(){
const userInfo = await getUserInfo();
// 对roles的操作要视具体情况而定,看后端返回的数据是怎样的,我这里自己模拟是数组中包含对象
const { roles = [] } = userInfo;
if(isArray(roles)){
const roleList = roles.map((item) => item.value);
this.setRoleList(roleList)
}else{
userInfo.roles = [];
this.setRoleList([])
}
this.setUserInfo(userInfo);
return userInfo
}
}
})
5. 最后
这只是一个整体的粗略框架,还有许多细节的地方需要完善,后续想到什么再做更新。