验证码
parent
306f658fbf
commit
4df4b5cdb8
|
|
@ -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>
|
||||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
];
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue