feat: 新增登录验证码
This commit is contained in:
parent
40c40fa90d
commit
9a297d1986
|
|
@ -1,6 +1,6 @@
|
|||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { LoginData, LoginResult } from './types';
|
||||
import { CaptchaResult, LoginData, LoginResult } from './types';
|
||||
|
||||
/**
|
||||
* 登录API
|
||||
|
|
@ -25,3 +25,15 @@ export function logoutApi() {
|
|||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
export function getCaptchaApi(): AxiosPromise<CaptchaResult> {
|
||||
return request({
|
||||
url: '/api/v1/auth/captcha',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,21 @@ export interface LoginData {
|
|||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username: string;
|
||||
username?: string;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
password: string;
|
||||
password?: string;
|
||||
|
||||
/**
|
||||
* 验证码缓存key
|
||||
*/
|
||||
verifyCodeKey?: string;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
verifyCode?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -33,3 +43,18 @@ export interface LoginResult {
|
|||
*/
|
||||
tokenType?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码响应
|
||||
*/
|
||||
export interface CaptchaResult {
|
||||
/**
|
||||
* 验证码缓存key
|
||||
*/
|
||||
verifyCodeKey: string;
|
||||
/**
|
||||
* 验证码图片Base64字符串
|
||||
*/
|
||||
verifyCodeBase64: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<svg t="1655050462467" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2131"
|
||||
width="200" height="200">
|
||||
<path
|
||||
d="M917.6 267.2c-36.1-2.5-72.4-9.3-103.6-19.3-10.1-3-20.2-6.4-30.3-10-21.4-6.3-50.5-18.8-83.6-36.6-0.4-0.2-0.7-0.4-1.1-0.6-7.8-4.2-15.7-8.7-23.8-13.4-10.9-6.3-21.7-12.9-32.5-19.9-0.4-0.3-0.8-0.5-1.2-0.8-7.7-5-15.5-10.2-23.1-15.5-5-3.4-10-7.1-15-10.7-3.8-2.8-7.5-5.3-11.3-8.2-27.4-20.5-54.5-43.5-79.9-68.3-25.4 24.8-52.5 47.8-79.9 68.3-3.7 2.8-7.5 5.4-11.3 8.2-5 3.6-10 7.3-15 10.7-7.7 5.4-15.4 10.5-23.1 15.5-0.4 0.3-0.8 0.5-1.2 0.8-10.8 6.9-21.6 13.6-32.5 19.9-8.1 4.7-16 9.2-23.8 13.4-0.3 0.2-0.7 0.4-1 0.6-33 17.8-62.2 30.3-83.6 36.6-10.1 3.6-20.2 7-30.3 10-31.1 10-67.4 16.8-103.6 19.3h0.1c1.1 16.2 2.1 37.7 3.4 60.9h0.7c6.1 86.8 23.5 210.2 49.7 282.8 1.2 3.2 2.2 6.5 3.3 9.6 0.6 1.5 1.2 2.8 1.8 4.3 62.8 162.1 171.9 280.1 303 323.4v0.4c17.3 5.7 31.9 9.3 43.5 11.5 11.5-2.2 26.1-5.8 43.5-11.5v-0.4C687 905 796.1 787 858.9 624.8c0.6-1.5 1.2-2.8 1.8-4.3 1.2-3.1 2.2-6.4 3.3-9.6 26.2-72.5 43.6-196 49.7-282.8h0.7c1.1-23.3 2.2-44.7 3.2-60.9z m-47.4 41.9l-0.5 9.5c-0.5 2.2-0.9 4.4-1 6.6C863 406 847 525.7 821.3 596.7c-0.7 1.9-1.4 3.9-2 5.8-0.4 1.2-0.8 2.5-1.4 4.1-0.5 1.2-1 2.5-1.4 3.4C758.1 760.8 657.7 869.3 541 907.8c-1.9 0.6-3.7 1.4-5.5 2.2-7.9 2.5-15.7 4.6-23.2 6.3-7.5-1.7-15.2-3.8-23.1-6.3-1.8-0.9-3.6-1.6-5.5-2.2-116.7-38.5-217.1-147-275.4-297.5-0.5-1.2-0.9-2.4-1.7-4.1-0.4-1.2-0.8-2.4-1.3-3.6-0.7-2-1.3-3.9-1.9-5.6-25.8-71.2-41.7-191-47.4-271.7-0.2-2.3-0.5-4.5-1-6.6l-0.5-9.3c-0.1-1.5-0.2-3-0.2-4.5 24.6-3.8 48.4-9.3 70-16.2 10.1-3 20.4-6.4 31.4-10.4 25.2-7.6 56.5-21.2 90.5-39.6 0.6-0.3 1.2-0.6 1.7-0.9 8.2-4.4 16.7-9.2 24.8-14 10.7-6.1 22-13 34.5-21.1 0.4-0.2 1-0.6 1.3-0.8 8.2-5.3 16.4-10.8 24.1-16.2 4.5-3.1 9.1-6.4 13.7-9.7l2.4-1.8 4-2.9c2.6-1.9 5.2-3.7 7.5-5.5 17.9-13.4 35.3-27.5 52-42.1 16.7 14.7 34 28.7 51.8 42 2.6 1.9 5.1 3.8 7.7 5.6l4.3 3.1 1.5 1.1c4.8 3.5 9.6 6.9 14 9.9 8.1 5.7 16.3 11.2 23.7 16l2.1 1.3c12.4 8 23.7 14.9 34.1 20.8 8.6 5 17 9.8 25 14.1 0.4 0.2 1 0.5 1.5 0.8 34.2 18.4 65.6 32.1 90.9 39.7 11 3.9 21.3 7.3 30.6 10.1 22.1 7.1 46.1 12.6 70.8 16.5 0.1 1.5 0.1 3 0 4.4z"
|
||||
p-id="2132"></path>
|
||||
<path
|
||||
d="M710.6 411.2L476.1 651.6l-120-123c-8.3-8.5-21.8-8.5-30.1 0s-8.3 22.3 0 30.9L461.1 698c4.2 4.3 9.6 6.4 15.1 6.4 5.4 0 10.9-2.1 15-6.4l249.5-255.7c8.3-8.5 8.3-22.3 0-30.9-8.3-8.7-21.8-8.7-30.1-0.2z"
|
||||
p-id="2133"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -10,10 +10,7 @@ export default {
|
|||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Login',
|
||||
code: 'Verification Code',
|
||||
copyright: '',
|
||||
icp: '',
|
||||
thirdPartyLogin: 'third-party login'
|
||||
verifyCode: 'Verify Code',
|
||||
},
|
||||
// 导航栏国际化
|
||||
navbar: {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ export default {
|
|||
username: '用户名',
|
||||
password: '密码',
|
||||
login: '登 录',
|
||||
code: '请输入验证码',
|
||||
copyright: '',
|
||||
icp: '',
|
||||
thirdPartyLogin: '第三方登录'
|
||||
verifyCode: '验证码'
|
||||
},
|
||||
navbar: {
|
||||
dashboard: '首页',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
<template>
|
||||
<div class="login-container">
|
||||
<el-form
|
||||
ref="loginFormRef"
|
||||
:model="loginData"
|
||||
:rules="loginRules"
|
||||
class="login-form"
|
||||
>
|
||||
<el-form ref="loginFormRef" :model="loginData" :rules="loginRules" class="login-form">
|
||||
<div class="flex text-white items-center py-4">
|
||||
<span class="text-2xl flex-1 text-center">{{ $t('login.title') }}</span>
|
||||
<lang-select style="color: #fff" />
|
||||
|
|
@ -15,51 +10,40 @@
|
|||
<div class="p-2 text-white">
|
||||
<svg-icon icon-class="user" />
|
||||
</div>
|
||||
<el-input
|
||||
class="flex-1"
|
||||
ref="username"
|
||||
size="large"
|
||||
v-model="loginData.username"
|
||||
:placeholder="$t('login.username')"
|
||||
name="username"
|
||||
/>
|
||||
<el-input class="flex-1" ref="username" size="large" v-model="loginData.username"
|
||||
:placeholder="$t('login.username')" name="username" />
|
||||
</el-form-item>
|
||||
|
||||
<el-tooltip
|
||||
:disabled="isCapslock === false"
|
||||
content="Caps lock is On"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip :disabled="isCapslock === false" content="Caps lock is On" placement="right">
|
||||
<el-form-item prop="password">
|
||||
<span class="p-2 text-white">
|
||||
<svg-icon icon-class="password" />
|
||||
</span>
|
||||
<el-input
|
||||
class="flex-1"
|
||||
v-model="loginData.password"
|
||||
placeholder="密码"
|
||||
:type="passwordVisible === false ? 'password' : 'input'"
|
||||
size="large"
|
||||
name="password"
|
||||
@keyup="checkCapslock"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
<el-input class="flex-1" v-model="loginData.password" placeholder="密码"
|
||||
:type="passwordVisible === false ? 'password' : 'input'" size="large" name="password" @keyup="checkCapslock"
|
||||
@keyup.enter="handleLogin" />
|
||||
<span class="mr-2" @click="passwordVisible = !passwordVisible">
|
||||
<svg-icon
|
||||
:icon-class="passwordVisible === false ? 'eye' : 'eye-open'"
|
||||
class="text-white cursor-pointer"
|
||||
/>
|
||||
<svg-icon :icon-class="passwordVisible === false ? 'eye' : 'eye-open'" class="text-white cursor-pointer" />
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button
|
||||
size="default"
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
class="w-full"
|
||||
@click.prevent="handleLogin"
|
||||
>{{ $t('login.login') }}
|
||||
<!-- 验证码 -->
|
||||
<el-form-item prop="verifyCode">
|
||||
<span class="p-2 text-white">
|
||||
<svg-icon icon-class="verify_code" />
|
||||
</span>
|
||||
<el-input v-model="loginData.verifyCode" auto-complete="off" :placeholder="$t('login.verifyCode')" class="w-[60%]"
|
||||
@keyup.enter="handleLogin" />
|
||||
|
||||
<div class="captcha">
|
||||
<img :src="captchaBase64" @click="getCaptcha" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-button size="default" :loading="loading" type="primary" class="w-full" @click.prevent="handleLogin">{{
|
||||
$t('login.login') }}
|
||||
</el-button>
|
||||
|
||||
<!-- 账号密码提示 -->
|
||||
|
|
@ -81,6 +65,7 @@ import { useUserStore } from '@/store/modules/user';
|
|||
|
||||
// API依赖
|
||||
import { LocationQuery, LocationQueryValue, useRoute } from 'vue-router';
|
||||
import { getCaptchaApi } from '@/api/auth';
|
||||
import { LoginData } from '@/api/auth/types';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
|
@ -90,15 +75,17 @@ const loginFormRef = ref(ElForm);
|
|||
|
||||
const loginData = ref<LoginData>({
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
password: '123456',
|
||||
});
|
||||
|
||||
const loginRules = {
|
||||
username: [{ required: true, trigger: 'blur' }],
|
||||
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
||||
password: [{ required: true, trigger: 'blur', validator: validatePassword }],
|
||||
// verifyCode: [{ required: true, trigger: 'blur' }],
|
||||
};
|
||||
|
||||
const passwordVisible = ref(false);
|
||||
const captchaBase64 = ref()
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
|
|
@ -119,7 +106,18 @@ function checkCapslock(e: any) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* 验证码
|
||||
*/
|
||||
function getCaptcha() {
|
||||
getCaptchaApi().then(({ data }) => {
|
||||
const { verifyCodeBase64, verifyCodeKey } = data;
|
||||
loginData.value.verifyCodeKey = verifyCodeKey;
|
||||
captchaBase64.value = verifyCodeBase64;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
function handleLogin() {
|
||||
loginFormRef.value.validate((valid: boolean) => {
|
||||
|
|
@ -150,9 +148,26 @@ function handleLogin() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCaptcha();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.captcha {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
img {
|
||||
height: 48px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.login-container {
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
|
|
@ -167,6 +182,7 @@ function handleLogin() {
|
|||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
|
|
@ -175,17 +191,20 @@ function handleLogin() {
|
|||
|
||||
.el-input {
|
||||
background: transparent;
|
||||
|
||||
// 子组件 scoped 无效,使用 :deep
|
||||
:deep(.el-input__wrapper) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
.el-input__inner {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
caret-color: #fff;
|
||||
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px transparent inset !important;
|
||||
-webkit-text-fill-color: #fff !important;
|
||||
|
|
@ -202,5 +221,6 @@ function handleLogin() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue