验证码

develop
ihzero 2023-12-06 17:36:54 +08:00
parent 306f658fbf
commit 4df4b5cdb8
4 changed files with 263 additions and 59 deletions

View File

@ -0,0 +1,138 @@
<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: 16,
},
fontSizeMax: {
type: Number,
default: 40,
},
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: 112,
},
contentHeight: {
type: Number,
default: 38,
},
},
watch: {
identifyCode() {
this.drawPic()
},
},
mounted() {
this.drawPic()
},
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 < 8; 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 < 100; 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()
}
},
},
}
</script>

View File

@ -25,6 +25,23 @@
/>
</FormItem>
<FormItem name="identifyCode" class="enter-x">
<div class="flex items-center w-full">
<div class="flex-1">
<Input
size="large"
v-model:value="formData.identifyCode"
placeholder="请输入验证码"
class="!w-full !min-w-full"
/>
</div>
<div @click="refreshCode">
<Security class="ml-1 flex-none" :identifyCode="identifyCode" />
</div>
</div>
</FormItem>
<!-- <ARow class="enter-x">
<ACol :span="12">
<FormItem>
@ -90,6 +107,7 @@
</Form>
</template>
<script lang="ts" setup>
import Security from '/@/components/Security/index.vue'
import { reactive, ref, unref, computed } from 'vue'
import md5 from 'js-md5'
import { Checkbox, Form, Input, Row, Col, Button, Divider } from 'ant-design-vue'
@ -125,10 +143,11 @@
const formRef = ref()
const loading = ref(false)
const rememberMe = ref(false)
const identifyCode = ref('')
const formData = reactive({
account: '',
password: '',
identifyCode: '',
})
const { validForm } = useFormValid(formRef)
@ -136,10 +155,38 @@
//onKeyStroke('Enter', handleLogin);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
function randomNum(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
function refreshCode() {
formData.identifyCode = ''
makeCode()
}
makeCode()
function makeCode(l = 4) {
const identifyCodes = '1234567890abcdef'
let code = ''
for (let i = 0; i < l; i++) {
code += identifyCodes[randomNum(0, identifyCodes.length)]
}
identifyCode.value = code
}
async function handleLogin(e) {
const data = await validForm()
if (!data) return
if (data.identifyCode.toLowerCase() !== identifyCode.value.toLowerCase()) {
createErrorModal({
title: '错误提示',
content: '验证码不正确',
})
return
}
try {
loading.value = true
const userInfo = await userStore.login({

View File

@ -66,39 +66,40 @@
</template>
</template>
<script lang="ts" setup>
import { reactive, ref, unref, computed } from 'vue';
import LoginFormTitle from './LoginFormTitle.vue';
import { Form, Input, Button, Checkbox } from 'ant-design-vue';
import { StrengthMeter } from '/@/components/StrengthMeter';
import { CountdownInput } from '/@/components/CountDown';
import { useI18n } from '/@/hooks/web/useI18n';
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
import { reactive, ref, unref, computed } from 'vue'
import LoginFormTitle from './LoginFormTitle.vue'
import { Form, Input, Button, Checkbox } from 'ant-design-vue'
import { StrengthMeter } from '/@/components/StrengthMeter'
import { CountdownInput } from '/@/components/CountDown'
import { useI18n } from '/@/hooks/web/useI18n'
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin'
const FormItem = Form.Item;
const InputPassword = Input.Password;
const { t } = useI18n();
const { handleBackLogin, getLoginState } = useLoginState();
const FormItem = Form.Item
const InputPassword = Input.Password
const { t } = useI18n()
const { handleBackLogin, getLoginState } = useLoginState()
const formRef = ref();
const loading = ref(false);
const formRef = ref()
const loading = ref(false)
const formData = reactive({
account: '',
password: '',
confirmPassword: '',
identifyCode: '',
mobile: '',
sms: '',
policy: false,
});
})
const { getFormRules } = useFormRules(formData);
const { validForm } = useFormValid(formRef);
const { getFormRules } = useFormRules(formData)
const { validForm } = useFormValid(formRef)
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
async function handleRegister() {
const data = await validForm();
if (!data) return;
console.log(data);
const data = await validForm()
if (!data) return
console.log(data)
}
</script>

View File

@ -1,7 +1,7 @@
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { RuleObject } from 'ant-design-vue/lib/form/interface';
import { ref, computed, unref, Ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import type { ValidationRule } from 'ant-design-vue/lib/form/Form'
import type { RuleObject } from 'ant-design-vue/lib/form/interface'
import { ref, computed, unref, Ref } from 'vue'
import { useI18n } from '/@/hooks/web/useI18n'
export enum LoginStateEnum {
LOGIN,
@ -11,67 +11,84 @@ export enum LoginStateEnum {
QR_CODE,
}
const currentState = ref(LoginStateEnum.LOGIN);
const currentState = ref(LoginStateEnum.LOGIN)
export function useLoginState() {
function setLoginState(state: LoginStateEnum) {
currentState.value = state;
currentState.value = state
}
const getLoginState = computed(() => currentState.value);
const getLoginState = computed(() => currentState.value)
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
setLoginState(LoginStateEnum.LOGIN)
}
return { setLoginState, getLoginState, handleBackLogin };
return { setLoginState, getLoginState, handleBackLogin }
}
export function useFormValid<T extends Object = any>(formRef: Ref<any>) {
async function validForm() {
const form = unref(formRef);
if (!form) return;
const data = await form.validate();
return data as T;
const form = unref(formRef)
if (!form) return
const data = await form.validate()
return data as T
}
return { validForm };
return { validForm }
}
export function useFormRules(formData?: Recordable) {
const { t } = useI18n();
const { t } = useI18n()
const getAccountFormRule = computed(() => createRule(t('sys.login.accountPlaceholder')));
const getPasswordFormRule = computed(() => createRule(t('sys.login.passwordPlaceholder')));
const getSmsFormRule = computed(() => createRule(t('sys.login.smsPlaceholder')));
const getMobileFormRule = computed(() => createRule(t('sys.login.mobilePlaceholder')));
const getAccountFormRule = computed(() => createRule(t('sys.login.accountPlaceholder')))
const getPasswordFormRule = computed(() => createRule(t('sys.login.passwordPlaceholder')))
const getIdentifyCodeFormRule = computed(() => createRule('请输入验证码'))
const getSmsFormRule = computed(() => createRule(t('sys.login.smsPlaceholder')))
const getMobileFormRule = computed(() => createRule(t('sys.login.mobilePlaceholder')))
const validatePolicy = async (_: RuleObject, value: boolean) => {
return !value ? Promise.reject(t('sys.login.policyPlaceholder')) : Promise.resolve();
};
return !value ? Promise.reject(t('sys.login.policyPlaceholder')) : Promise.resolve()
}
const validateConfirmPassword = (password: string) => {
return async (_: RuleObject, value: string) => {
if (!value) {
return Promise.reject(t('sys.login.passwordPlaceholder'));
return Promise.reject(t('sys.login.passwordPlaceholder'))
}
if (value !== password) {
return Promise.reject(t('sys.login.diffPwd'));
return Promise.reject(t('sys.login.diffPwd'))
}
return Promise.resolve();
};
};
return Promise.resolve()
}
}
//校验验证码
const validateIdentifyCode = (validate: string) => {
return async (_: RuleObject, value: string) => {
console.log(validate)
if (!value) {
return Promise.reject('请输入验证码')
}
if (value !== validate) {
return Promise.reject('验证码不正确')
}
return Promise.resolve()
}
}
const getFormRules = computed((): { [k: string]: ValidationRule | ValidationRule[] } => {
const accountFormRule = unref(getAccountFormRule);
const passwordFormRule = unref(getPasswordFormRule);
const smsFormRule = unref(getSmsFormRule);
const mobileFormRule = unref(getMobileFormRule);
const accountFormRule = unref(getAccountFormRule)
const passwordFormRule = unref(getPasswordFormRule)
const identifyFormRule = unref(getIdentifyCodeFormRule)
const smsFormRule = unref(getSmsFormRule)
const mobileFormRule = unref(getMobileFormRule)
const mobileRule = {
sms: smsFormRule,
mobile: mobileFormRule,
};
}
switch (unref(currentState)) {
// register form rules
case LoginStateEnum.REGISTER:
@ -83,28 +100,29 @@ export function useFormRules(formData?: Recordable) {
],
policy: [{ validator: validatePolicy, trigger: 'change' }],
...mobileRule,
};
}
// reset password form rules
case LoginStateEnum.RESET_PASSWORD:
return {
account: accountFormRule,
...mobileRule,
};
}
// mobile form rules
case LoginStateEnum.MOBILE:
return mobileRule;
return mobileRule
// login form rules
default:
return {
account: accountFormRule,
password: passwordFormRule,
};
identifyCode: identifyFormRule,
}
}
});
return { getFormRules };
})
return { getFormRules }
}
function createRule(message: string) {
@ -114,5 +132,5 @@ function createRule(message: string) {
message,
trigger: 'change',
},
];
]
}