實現圖如下(忽略樣式):
功能:對表單驗證,點擊右側驗證碼圖片可以隨機切換更新。
實現代碼
HTML:
<template> <div style="width: 100%;height: 100vh; background-color: blueviolet;overflow: hidden"> <div style="width: 400px; margin: 150px auto"> <div style="color: #ccc; font-size: 30px; text-align: center; padding: 30px 0">歡迎登錄</div> <div v-if="loginType=='denglu'"> <el-form ref="form" :model="form" size="normal" :rules="rules"> <el-form-item prop="username"> <el-input prefix-icon="el-icon-user-solid" placeholder="請輸入用戶名" v-model="form.username"></el-input> </el-form-item> <el-form-item prop="password"> <el-input prefix-icon="el-icon-lock" placeholder="請輸入密碼" v-model="form.password" show-password ></el-input> </el-form-item> <el-form-item > <el-button style="width: 100%;" type="primary" @click="login(form)">登錄</el-button> </el-form-item> <!-- <el-form-item >--> <!-- <el-button style="width: 100%;" type="primary" >立即注冊</el-button>--> <!-- </el-form-item>--> <div style="display: flex; justify-content: center"> <div style="font-size: 14px;color: blue;border-bottom: 1px solid blue;width: 73px;height: 23px;margin-right: 100px">第三方登錄</div> <div style="font-size: 14px;color: blue;border-bottom: 1px solid blue;width: 73px;height: 23px;" @click="refresh">驗證碼登錄</div> </div> </el-form> </div> <div v-else> <el-form ref="form" :model="form" size="normal" :rules="rules"> <el-form-item prop="telephone"> <el-input prefix-icon="el-icon-user-solid" placeholder="請輸入手機號" v-model="form.telephone"></el-input> </el-form-item> <el-form-item > <div style="display: flex"> <div style="width: 200px; margin-right: 100px"> <el-input prefix-icon="el-icon-lock" placeholder="請輸入驗證碼" v-model="code" ></el-input> </div> <div class="login-code" style="cursor: pointer" @click="refreshCode"><s-identify :identifyCode="identifyCode"></s-identify></div> </div> </el-form-item> <el-form-item > <el-button style="width: 100%;" type="primary" @click="login(form)">登錄</el-button> </el-form-item> <!-- <el-form-item >--> <!-- <el-button style="width: 100%;" type="primary" >立即注冊</el-button>--> <!-- </el-form-item>--> <div style="display: flex; justify-content: center"> <div style="font-size: 14px;color: blue;border-bottom: 1px solid blue;width: 73px;height: 23px;margin-right: 100px">第三方登錄</div> <div style="font-size: 14px;color: blue;border-bottom: 1px solid blue;width: 90px;height: 23px;" @click="refresh">賬號密碼登錄</div> </div> </el-form> </div> </div> </div> </template>
JS:
import {ElMessage} from "element-plus"; import request from "../utils/request"; import SIdentify from "../components/SIdentify.vue"; export default { name: "Layout", components: { SIdentify }, data() { return { form: {}, loginType: 'yanzhengma', identifyCode: '', identifyCodes: '1234567890', code: '', rules: { username: { required: true, message: '請輸入名稱', trigger: 'blur', }, password: { required: true, message: '請輸入密碼', trigger: 'blur', }, telephone: { required: true, message: '請輸入手機號', trigger: 'blur', } } } }, created() { sessionStorage.removeItem("user"); this.refreshCode(); }, mounted() { this.identifyCode = ""; this.makeCode(this.identifyCodes, 4); }, methods: { refreshCode() { this.identifyCode = ''; this.makeCode(this.identifyCodes, 4); }, makeCode(o, l) { for (let i = 0; i < l; i++) { this.identifyCode += this.identifyCodes[ this.randomNum(0, this.identifyCodes.length)] } console.log(this.identifyCode); }, randomNum(min, max) { return Math.floor(Math.random() * (max - min) + min); }, refresh() { if (this.loginType == 'yanzhengma') { this.loginType = 'denglu' } else { this.loginType = 'yanzhengma' } }, login(form) { if (this.loginType == 'yanzhengma' && this.code == '') { ElMessage({ message: "請輸入驗證碼", type: 'error', }) return } if (this.loginType == 'yanzhengma' && this.identifyCode !== this.code) { this.code = ''; this.refreshCode(); ElMessage({ message: "請輸入正確的驗證碼", type: 'error', }) return; } //請求合法才可以 this.$refs.form.validate((valid) => { if (valid) { request.post("/user/login", this.form).then(res => { console.log(res) if (res.code === '0') { ElMessage({ message: "登錄成功", type: 'success', }) sessionStorage.setItem("user", JSON.stringify(res.data)) this.$store.commit('imageUrl', res.data.imageUrl) console.log(this.$store.state.imageUrl); //登錄成功后頁面跳轉到主頁 this.$router.push("/") } else { ElMessage({ message: res.msg, type: 'error', }) } }) } }) } } }
component:
SIdentity.vue:
<template> <div class="s-canvas"> <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas> </div> </template> <script> export default { name: 'SIdentify', props: { identifyCode: { type: String, default: '1234' }, fontSizeMin: { type: Number, default: 25 }, fontSizeMax: { type: Number, default: 30 }, backgroundColorMin: { type: Number, default: 255 }, backgroundColorMax: { type: Number, default: 255 }, colorMin: { type: Number, default: 0 }, colorMax: { type: Number, default: 160 }, lineColorMin: { type: Number, default: 100 }, lineColorMax: { type: Number, default: 255 }, dotColorMin: { type: Number, default: 0 }, dotColorMax: { type: Number, default: 255 }, contentWidth: { type: Number, default: 112 }, contentHeight: { type: Number, default: 31 } }, methods: { // 生成一個隨機數 randomNum(min, max) { return Math.floor(Math.random() * (max - min) + min) }, // 生成一個隨機的顏色 randomColor(min, max) { let r = this.randomNum(min, max) let g = this.randomNum(min, max) let b = this.randomNum(min, max) return 'rgb(' + r + ',' + g + ',' + b + ')' }, drawPic() { let canvas = document.getElementById('s-canvas') let ctx = canvas.getContext('2d') ctx.textBaseline = 'bottom' // 繪制背景 ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax) ctx.fillRect(0, 0, this.contentWidth, this.contentHeight) // 繪制文字 for (let i = 0; i < this.identifyCode.length; i++) { this.drawText(ctx, this.identifyCode[i], i) } this.drawLine(ctx) this.drawDot(ctx) }, drawText(ctx, txt, i) { ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax) ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1)) let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5) var deg = this.randomNum(-45, 45) // 修改坐標原點和旋轉角度 ctx.translate(x, y) ctx.rotate(deg * Math.PI / 180) ctx.fillText(txt, 0, 0) // 恢復坐標原點和旋轉角度 ctx.rotate(-deg * Math.PI / 180) ctx.translate(-x, -y) }, drawLine(ctx) { // 繪制干擾線 for (let i = 0; i < 5; i++) { ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax) ctx.beginPath() ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight)) ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight)) ctx.stroke() } }, drawDot(ctx) { // 繪制干擾點 for (let i = 0; i < 80; i++) { ctx.fillStyle = this.randomColor(0, 255) ctx.beginPath() ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI) ctx.fill() } } }, watch: { identifyCode() { this.drawPic() } }, mounted() { this.drawPic() } } </script> <style scoped> .s-canvas { height: 38px; } .s-canvas canvas{ margin-top: 1px; margin-left: 8px; } </style>