基于 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 = trueaxios.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 的项目之旅吧~
源码在下方 ↓【回复:记账本】即可