安装插件
pnpm i identify --save
图形验证码组件
<template>
<div class="s-canvas">
<!-- 图形验证码的宽和高都来自于父组件的传值,若父组件没有传值,那么就按当前子组件的默认值进行渲染 -->
<canvas id="s-canvas" :width="props.contentWidth" :height="props.contentHeight"></canvas>
</div>
</template>
<script setup>
import { onMounted, watch } from 'vue'
// 这里有很多属性值,需要自定义什么值就在父组件传对应参数即可
const props = defineProps({
identifyCode: {
type: String,
default: '1234'
},
fontSizeMin: {
type: Number,
default: 20
},
fontSizeMax: {
type: Number,
default: 35
},
backgroundColorMin: {
type: Number,
default: 180
},
backgroundColorMax: {
type: Number,
default: 240
},
colorMin: {
type: Number,
default: 50
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 40
},
lineColorMax: {
type: Number,
default: 180
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default: 71
},
contentHeight: {
type: Number,
default: 28
}
})
// 生成一个随机数
const randomNum = (min, max) => {
return Math.floor(Math.random() * (max - min) + min)
}
// 生成一个随机的颜色
const randomColor = (min, max) => {
let r = randomNum(min, max)
let g = randomNum(min, max)
let b = randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
}
// 绘制干扰线
const drawLine = (ctx) => {
for (let i = 0; i < 3; i++) {
ctx.strokeStyle = randomColor(props.lineColorMin, props.lineColorMax)
ctx.beginPath()
ctx.moveTo(randomNum(0, props.contentWidth), randomNum(0, props.contentHeight))
ctx.lineTo(randomNum(0, props.contentWidth), randomNum(0, props.contentHeight))
ctx.stroke()
}
}
const drawText = (ctx, txt, i) => {
ctx.fillStyle = randomColor(props.colorMin, props.colorMax)
ctx.font = randomNum(props.fontSizeMin, props.fontSizeMax) + 'px SimHei'
let x = (i + 1) * (props.contentWidth / (props.identifyCode.length + 1))
let y = randomNum(props.fontSizeMax, props.contentHeight - 5)
var deg = 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)
}
const drawDot = (ctx) => {
// 绘制干扰点
for (let i = 0; i < 30; i++) {
ctx.fillStyle = randomColor(0, 255)
ctx.beginPath()
ctx.arc(randomNum(0, props.contentWidth), randomNum(0, props.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
}
const drawPic = () => {
let canvas = document.getElementById('s-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
// 绘制背景
ctx.fillStyle = randomColor(props.backgroundColorMin, props.backgroundColorMax)
ctx.fillRect(0, 0, props.contentWidth, props.contentHeight)
// 绘制文字
for (let i = 0; i < props.identifyCode.length; i++) {
drawText(ctx, props.identifyCode[i], i)
}
drawLine(ctx)
drawDot(ctx)
}
// newValue, oldValue
watch(
() => props.identifyCode,
() => {
drawPic()
}
)
onMounted(() => {
drawPic()
})
</script>
<style scoped lang="scss">
.s-canvas {
cursor: pointer;
}
</style>
父组件中使用
html部分:验证码输入框+图形验证码+提示
<el-form-item prop="password">
<el-input
v-model="verificationCode"
class="elinput"
placeholder="请输入验证码"
prefix-icon="Key"
/>
</el-form-item>
<div
style="
text-align: right;
margin-top: -10px;
margin-bottom: 10px;
position: relative;
height: 40px;
"
@click="refreshCode"
>
<div style="position: absolute; border-radius: 5px; left: 1px">
<randomImage
:identify-code="identifyCode"
:content-width="110"
:content-height="40"
></randomImage>
</div>
<div style="position: absolute; left: 130px; bottom: -10px">
<p style="font-size: 12px; color: #67c23a">看不清?点击图片可进行切换哦!</p>
</div>
</div>
ts部分:引入组件+对应参数定义+登录验证
// 图形验证码
import randomImage from './components/randomImage.vue'
// 验证码数字库
const identifyCodes = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
// 图形验证码图片中的验证码,用于校验
const identifyCode = ref('')
// 输入的验证码
const verificationCode = ref('')
const randomNum = (min: any, max: any) => {
return Math.floor(Math.random() * (max - min) + min)
}
const makeCode = (o: any, l: any) => {
for (let i = 0; i < l; i++) {
identifyCode.value += o[randomNum(0, o.length)]
}
}
const refreshCode = () => {
identifyCode.value = ''
makeCode(identifyCodes, 4)
}
// 登录提交按钮所触发的事件;前端先校验,无误后再走接口;有错误则提示对应错误
const onSubmit = (e: Event) => {
e.preventDefault()
if (verificationCode.value === identifyCode.value) {
formRef.value?.validate((valid) => {
if (valid) {
loading.value = true
userStore
.login(form as any)
.then(() => {
ElMessage.success('登录成功')
router.push('/workplace')
})
.finally(() => {
loading.value = false
})
}
})
} else if (verificationCode.value === '') {
ElNotification({
title: '小提示',
message: '请输入验证码',
type: 'warning'
})
verificationCode.value = ''
refreshCode()
} else {
ElNotification({
title: '小提示',
message: '验证码输入错误,请重新输入',
type: 'error'
})
verificationCode.value = ''
refreshCode()
}
}
// 页面初始化时,执行一次逻辑生成图形验证码
onMounted(() => {
identifyCode.value = ''
makeCode(identifyCodes, 4)
})