irpas技术客

Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(登录注册页面,验证码)_前端杂货铺

irpas 3079

基于 Vue3.x + Vant UI 的多功能记账本(四)


文章目录 基于 Vue3.x + Vant UI 的多功能记账本(四)项目演示1、登录注册页面2、图片验证码3、修改 axios4、写到最后(附源码)

系列内容参考链接基于 Vue3.x + Vant UI 的多功能记账本(一)项目演示,涉及知识点基于 Vue3.x + Vant UI 的多功能记账本(二)搭建开发环境基于 Vue3.x + Vant UI 的多功能记账本(三)开发导航栏及公共部分

项目演示

Vue3 + Vant UI_多功能记账本

1、登录注册页面

页面设计,页面跳转

Login.vue

<template> <!-- 根据页面显示相应头部 --> <Header :title="type == 'login' ? '登录' : '注册'" /> <div class="auth"> <img class="logo" src="//s.yezgea02.com/1606836859539/onpeice.png" alt="" /> <!-- 登录界面的表单 --> <van-form class="form-wrap" @submit="onSubmit" v-if="type == 'login'"> <div class="form"> <!-- 账号输入框,clearable:清除图标,rules:表单校验规则 --> <van-field clearable v-model="username" name="username" label="账号" placeholder="请输入账号" :rules="[{ required: true, message: '请填写账户' }]" /> <!-- 密码输入框 --> <van-field clearable v-model="password" type="password" name="password" label="密码" placeholder="请输入密码" :rules="[{ required: true, message: '请填写密码' }]" /> </div> <div style="margin: 16px 0"> <van-button round block type="primary" native-type="submit"> 登录 </van-button> <p @click="chanegType('register')" class="change-btn"> 没有账号,前往注册 </p> </div> </van-form> <!-- 注册页面的表单 --> <van-form class="form-wrap" @submit="onSubmit" v-if="type == 'register'"> <div class="form"> <van-field clearable v-model="username" name="username" label="账号" placeholder="请输入账号" :rules="[{ required: true, message: '请填写账号' }]" /> <van-field clearable v-model="password" type="password" name="password" label="密码" placeholder="请输入密码" :rules="[{ required: true, message: '请填写密码' }]" /> <!-- 验证码输入框 --> <van-field center clearable label="验证码" placeholder="输入验证码" v-model="verify" > <!-- 点击刷新验证码 --> <template #button> <!-- 生成验证码图片组件,ref 方便拿到组件内的实例属性 --> <VueImgVerify ref="verifyRef" /> </template> </van-field> </div> <div style="margin: 16px 0"> <van-button round block type="primary" native-type="submit"> 注册 </van-button> <p @click="chanegType('login')" class="change-btn">登录已有账号</p> </div> </van-form> </div> </template> <script> import { reactive, toRefs, ref, onMounted } from "vue"; // 生成验证码的组件 import VueImgVerify from "../components/VueImageVerify.vue"; import Header from "../components/Header.vue"; import axios from "../utils/axios"; // 轻提示(成功/失败...) import { Toast } from "vant"; import router from "../router"; export default { name: "Login", components: { VueImgVerify, // 验证码组件 Header, //公共头组件 }, setup() { // 便于拿到 verifyRef 组件内的实例属性 const verifyRef = ref(null); // 注册登录的相关内容 const state = reactive({ username: "", password: "", type: "login", // 登录注册模式切换参数 verify: "", // 验证码输入框输入的内容 imgCode: "", // 生成的验证图片内的文字 }); console.log("verifyRef", verifyRef); // 提交登录 or 注册表单 const onSubmit = async (values) => { // 登录功能 if (state.type == "login") { const { data } = await axios.post("/user/login", { username: state.username, password: state.password, }); // 添加 token 到本地存储 localStorage.setItem("token", data.token); window.location.href = "/"; } else { // 生成的图片验证码的文字等于验证码组件生成的验证码 state.imgCode = verifyRef.value.imgCode || ""; // 如果验证码组件生成的验证码的小写 != 用户输入的验证码的小写,则提示错误 if ( verifyRef.value.imgCode.toLowerCase() != state.verify.toLowerCase() ) { console.log("verifyRef.value.imgCode", verifyRef.value.imgCode); Toast.fail("验证码错误"); return; } // 验证码匹配成功,注册=>注册成功 await axios.post("/user/register", { username: state.username, password: state.password, }); Toast.success("注册成功"); } }; // 切换登录和注册两种模式 const chanegType = (type) => { state.type = type; }; return { ...toRefs(state), onSubmit, chanegType, verifyRef, }; }, }; </script> <style lang='less' scoped> @import url("../config/custom.less"); .auth { height: calc(~"(100% - 46px)"); padding: 30px 20px 0 20px; background: @primary-bg; .logo { width: 150px; display: block; margin: 0 auto; margin-bottom: 30px; } .form-wrap { .form { border-radius: 10px; overflow: hidden; .van-cell:first-child { padding-top: 20px; } .van-cell:last-child { padding-bottom: 20px; } } } .change-btn { text-align: center; margin: 10px 0; color: @link-color; font-size: 14px; } } </style>

在 custom.less 下补充 link-color 变量的定义,在写样式的时候,以 color: @link-color; 这样的形式引用它

custom.less

@primary: #39be77; // 主题色 @danger: #fc3c0c; @primary-bg: #f5f5f5; @link-color: #597fe7;

当前页面的外层是 #app、body,作为父级,它们需要先把高度撑开

index.css

body, html, p { height: 100%; margin: 0; padding: 0; } * { box-sizing: border-box; } #app { height: 100%; }

此时,yarn dev,打开浏览器可以看到…

2、图片验证码

注:验证码基本上都是由服务端接口提供,然后上报之后由服务端验证是否正确,所以此部分内容可以自行选择是否去做。

<template> <div class="img-verify"> <!-- 画布,绑定一个点击事件,用于刷新验证码 --> <canvas ref="verify" :width="width" :height="height" @click="handleDraw" ></canvas> </div> </template> <script type="text/ecmascript-6"> import { reactive, onMounted, ref, toRefs } from "vue"; export default { setup() { const verify = ref(null); const state = reactive({ pool: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", // 字符串 width: 120, height: 40, imgCode: "", // 初始化验证码为空 }); onMounted(() => { // 初始化绘制图片验证码 state.imgCode = draw(); }); // 点击图片重新绘制 const handleDraw = () => { state.imgCode = draw(); }; // 随机数 const randomNum = (min, max) => { return parseInt(Math.random() * (max - min) + min); }; // 随机颜色 const randomColor = (min, max) => { const r = randomNum(min, max); const g = randomNum(min, max); const b = randomNum(min, max); return `rgb(${r},${g},${b})`; }; // 绘制图片 const draw = () => { // 3.填充背景颜色,背景颜色要浅一点 const ctx = verify.value.getContext("2d"); // 填充颜色 ctx.fillStyle = randomColor(180, 230); // 填充的位置 ctx.fillRect(0, 0, state.width, state.height); // 定义paramText let imgCode = ""; // 4.随机产生字符串,并且随机旋转 for (let i = 0; i < 4; i++) { // 随机的四个字 const text = state.pool[randomNum(0, state.pool.length)]; imgCode += text; // 随机的字体大小 const fontSize = randomNum(18, 40); // 字体随机的旋转角度 const deg = randomNum(-30, 30); /* * 绘制文字并让四个文字在不同的位置显示的思路 : * 1、定义字体 * 2、定义对齐方式 * 3、填充不同的颜色 * 4、保存当前的状态(以防止以上的状态受影响) * 5、平移 translate() * 6、旋转 rotate() * 7、填充文字 * 8、restore 出栈 * */ ctx.font = fontSize + "px Simhei"; ctx.textBaseline = "top"; ctx.fillStyle = randomColor(80, 150); /* * save() 方法把当前状态的一份拷贝压入到一个保存图像状态的栈中。 * 这就允许您临时地改变图像状态, * 然后,通过调用 restore() 来恢复以前的值。 * save是入栈,restore 是出栈。 * 用来保存Canvas的状态。save 之后,可以调用 Canvas 的平移、放缩、旋转、错切、裁剪等操作。 restore:用来恢复 Canvas 之前保存的状态。防止 save 后对 Canvas 执行的操作对后续的绘制有影响。 * * */ ctx.save(); ctx.translate(30 * i + 15, 15); ctx.rotate((deg * Math.PI) / 180); // fillText() 方法在画布上绘制填色的文本。文本的默认颜色是黑色。 // 请使用 font 属性来定义字体和字号,并使用 fillStyle 属性以另一种颜色/渐变来渲染文本。 // context.fillText(text,x,y,maxWidth); ctx.fillText(text, -15 + 5, -15); ctx.restore(); } // 5.随机产生5条干扰线,干扰线的颜色要浅一点 for (let i = 0; i < 5; i++) { ctx.beginPath(); ctx.moveTo(randomNum(0, state.width), randomNum(0, state.height)); ctx.lineTo(randomNum(0, state.width), randomNum(0, state.height)); ctx.strokeStyle = randomColor(180, 230); ctx.closePath(); ctx.stroke(); } // 6.随机产生40个干扰的小点 for (let i = 0; i < 40; i++) { ctx.beginPath(); ctx.arc( randomNum(0, state.width), randomNum(0, state.height), 1, 0, 2 * Math.PI ); ctx.closePath(); ctx.fillStyle = randomColor(150, 200); ctx.fill(); } return imgCode; }; return { ...toRefs(state), verify, handleDraw, }; }, }; </script> <style type="text/css"> .img-verify canvas { cursor: pointer; } </style>

此时,yarn dev,打开浏览器可以看到…

3、修改 axios

为避免在页面内请求接口的时候,每次都通过 code 码去判断接口请求是否成功,我们可以这样修改 axios.js 文件

axios.js

import axios from 'axios' // 轻提示插件(Vant UI) import { Toast } from 'vant' import router from '../router' // 根据环境变量切换本地和线上的请求地址 axios.defaults.baseURL = process.env.NODE_ENV == 'development' ? '/api' : '//47.99.134.126:7008/api' // 允许跨域 axios.defaults.withCredentials = true axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest' // token的用户鉴权方式,在请求头的 headers 内添加 token,每次请求都会验证用户信息 axios.defaults.headers['Authorization'] = `${localStorage.getItem('token') || null}` axios.defaults.headers.post['Content-Type'] = 'application/json' axios.interceptors.response.use(res => { // 返回数据的类型不是对象,则报异常 if (typeof res.data !== 'object') { Toast.fail('服务端异常!') return Promise.reject(res) } // code 状态码不是200,则报异常 if (res.data.code != 200) { if (res.data.msg) Toast.fail(res.data.msg) // code 状态码为 401 代表接口需要登录,继而跳转到登录页面 if (res.data.code == 401) { router.push({ path: '/login' }) } // 返回失败的实例 return Promise.reject(res.data) } // code 为 200 时,请求成功,返回数据 return res.data }) export default axios 4、写到最后(附源码)

看到这么好的项目,是不是有种想自己做出来的冲动?

如果有,那么说明你非常的想提升自己,想检验自己这段时间的学习成果,这个项目绝对是你的 不二选择

心动不如行动

那么接下来,一起从0搭建,开始我们基于 Vue3.x + Vant UI 的项目之旅吧~

源码在下方 ↓【回复:记账本】即可


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Vue项目实战基于 #Vue3x #vant #验证码