构建简单的登录页
新建src/api/login.ts
import request from '@/utils/request'
export default {
/**
* 登录
* @param data {username: string; password: string }
* @returns
*/
login: (data: { username: string; password: string }) => request({ url: '/user/login', method: 'post', data }),
}
login页面
<template>
<div class="login-form">
<el-form :model="form" ref="loginFormRef" :rules="rules">
<el-form-item label="" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名"> </el-input>
</el-form-item>
<el-form-item label="" prop="password">
<el-input type="password" v-model="form.password" placeholder="请输入密码"> </el-input>
</el-form-item>
<el-form-item>
<el-button class="login-form--submit" type="primary" size="large" @click="submitForm" :loading="loading">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import api from '@/api/login'
const form = ref<{
username: string
password: string
}>({ username: '', password: '' })
const loading = ref<boolean>(false)
const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}
const loginFormRef = ref(null as any)
const submitForm = async () => {
const res = await loginFormRef.value.validate()
if (!res) return
try {
loading.value = true
const res = await api.login({ ...form.value })
console.log('🚀 ~ submitForm ~ res:', res)
} catch (error) {
} finally {
loading.value = false
}
}
</script>
<style lang="scss" scoped>
.login-form {
width: 300px;
&--submit {
width: 100%;
}
}
</style>
这个时候时没有数据 需要我们新增数据
注册功能的实现
注册接口需要用到短信或者邮箱发送短信的功能 这里由于我比较穷就使用邮箱服务就行
这只是一个简陋的注册 只是实现功能需求
在node中
// 安装nodemailer
pnpm i nodemailer
const nodemailer = require('nodemailer') //引入模块
// 获取邮箱验证码
let transporter = nodemailer.createTransport({
service: 'qq', // 类型qq邮箱
port: 465,
secure: true,
auth: { user: 'xxxx@qq.com', pass: 'xxxxxxx' }
})
function sendMail(email, code, call) {
// 发送的配置项
let mailOptions = {
from: 'xxxx@qq.com', // 发送者
to: email, // 接受者,可以同时发送多个,以逗号隔开
subject: 'vue3admin', // 标题
html: `<h2>欢迎登录:本次的验证码是${code}</h2>`
}
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
call(false)
} else {
call(true) //因为是异步 所有需要回调函数通知成功结果
}
})
}
// 根据邮箱获取code
// 设置一个定时器 定时清除check和邮箱的对应关系
const check = {}
router.get('/code', function (req, res, next) {
// 获取邮箱
if (!req.query.email) return res.send({ code: 400, message: '缺少必填参数' })
let email = req.query.email
let code = Math.random().toString().slice(2, 6)
check[email] = code // 邮箱和验证码对应关系
sendMail(email, code, function (result) {
if (result) {
// 获取邮箱成功 开启定时器
setTimeout(() => {
delete check[email]
console.log(`成功清除${email}code的对应关系`)
}, 60000)
res.send({ code: 200, message: '验证码发送成功' })
} else {
res.send({ code: 400, message: '验证码发送失败' })
}
})
})
// 注册用户
router.post('/register', async function (req, res, next) {
// 必填账号密码邮箱
const { username, password, email, code } = req.body
if (!username || !password || !email || !code) return res.send({ code: 400, message: '缺少必填参数' })
try {
// 判断用户是否存在
const user = await User.findOne({ username })
if (user) return res.send({ code: 400, message: '该账号已存在' })
// 判断邮箱验证码是否正确 失效时间1分钟
if (check[email] !== code) return res.send({ code: 400, message: '验证码错误' })
await User.create({ ...req.body, id: $generateUUID() }) // 创建新用户
res.send({ code: 200, message: '注册成功' })
} catch (error) {
res.send({ code: 500, message: error })
}
})
在vue3中
注册对应的路由和页面 /register
/**
* 注册
* @param data {username: string; password: string; code: string; email: string }
* @returns
*/
register: (data: { username: string; password: string; code: string; email: string }) => request({ url: '/user/register', method: 'post', data }),
/login/register.vue
<template>
<el-form :model="form" ref="registerFormRef" :rules="rules" class="register-form">
<el-form-item label="" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名"> </el-input>
</el-form-item>
<el-form-item label="" prop="password">
<el-input type="password" v-model="form.password" placeholder="请输入密码" autocomplete="new-password" show-password> </el-input>
</el-form-item>
<!-- 邮箱 -->
<el-form-item label="" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱"> </el-input>
</el-form-item>
<!-- 获取邮箱code 点击发送 -->
<el-form-item label="" prop="code">
<div class="sendEmailCode">
<el-input v-model="form.code" placeholder="请输入邮箱验证码"> </el-input>
<el-button type="primary" @click="sendEmailCode" :disabled="!disabled">{{ isCountDown ? '发送验证码' : `请等待${count}秒重试` }}</el-button>
</div>
</el-form-item>
<el-form-item label="" prop="emailCode">
<!-- 注册 -->
<el-button type="primary" @click="register" size="large" style="width: 100%">注册</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import api from '@/api/login'
const form = ref({ username: '', password: '', email: '', code: '' })
const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
}
const isEmail = (str: string) => {
const emailRegular = new RegExp(/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/)
return emailRegular.test(str)
}
// 判断邮箱是否为空且格式正确
const disabled = computed(() => {
return form.value.email && isEmail(form.value.email) && isCountDown.value
})
// 显示倒数计时
const isCountDown = ref(true)
const count = ref(60)
const sendEmailCode = async () => {
isCountDown.value = false
countDown()
// 发送邮箱验证码
await api.getEmailCode({ email: form.value.email })
}
// 倒计时函数
const countDown = () => {
if (count.value > 0) {
count.value--
setTimeout(() => {
countDown()
}, 1000)
} else {
isCountDown.value = true
count.value = 60
}
}
// 注册
const registerFormRef = ref(null as any)
const register = async () => {
const res = await registerFormRef.value.validate()
if (!res) return
try {
await api.register(form.value)
} catch (error) {}
}
</script>
<style lang="scss" scoped>
.sendEmailCode {
display: flex;
justify-content: space-between;
gap: 10px;
}
</style>
这样就可以成功的注册了 我们去登录就可以看
完善登录页面 登录成功后我们需要保存token到pinia中 在后面的登录中带上token请求
store/modules/main.ts
import { defineStore } from 'pinia'
import api from '@/api/login'
export const useMainStore = defineStore('main', {
state: () => {
return {
token: '',
userInfo: {},
}
},
getters: {
tokenGetter: state => state.token,
},
actions: {
// 设置token
async setToken(data: any) {
if (!data.token) return
this.token = data.token
const res = await api.getUserDetail(data.id)
this.userInfo = res.data || {}
},
},
persist: true`,`
})
store/index.ts
这样方便统一导出
import { createPinia } from 'pinia'
const pinia = createPinia()
// pinia-plugin-persistedstate 插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
pinia.use(piniaPluginPersistedstate)
export { useMainStore } from './modules/main.ts'
export default pinia
// 登录页
import { useMainStore } from '@/store'
mainStore.setToken(res.data)
// aioxs封装
// 在请求发送之前可以做一些处理,比如添加请求头等
const storeMain = useMainStore()
config.headers!.token = storeMain.tokenGetter
这样的话完整的登录和注册就可以实现
最后
欢迎 点赞 收藏 有问题欢迎留言评论!!