保存进度
This commit is contained in:
parent
bd9987d600
commit
40fec98273
|
@ -6,13 +6,13 @@ const router = createRouter({
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'Login',
|
||||||
component: () => import('../views/LoginView.vue'),
|
component: () => import('../views/LoginView.vue'),
|
||||||
meta: { requiresAuth: false }
|
meta: { requiresAuth: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
name: 'register',
|
name: 'Register',
|
||||||
component: () => import('../views/RegisterView.vue'),
|
component: () => import('../views/RegisterView.vue'),
|
||||||
meta: { requiresAuth: false }
|
meta: { requiresAuth: false }
|
||||||
},
|
},
|
||||||
|
|
|
@ -171,7 +171,7 @@
|
||||||
|
|
||||||
<div class="register-link">
|
<div class="register-link">
|
||||||
还没有账号?
|
还没有账号?
|
||||||
<router-link to="/register" class="register-btn">立即注册</router-link>
|
<router-link to="/register">立即注册</router-link>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,43 @@
|
||||||
已有账号?
|
已有账号?
|
||||||
<router-link to="/login" class="login-btn">立即登录</router-link>
|
<router-link to="/login" class="login-btn">立即登录</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 滑块验证码对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="sliderVisible"
|
||||||
|
title="安全验证"
|
||||||
|
width="420px"
|
||||||
|
:show-close="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
custom-class="slider-dialog"
|
||||||
|
>
|
||||||
|
<div class="slider-container">
|
||||||
|
<div class="slider-header">
|
||||||
|
<div class="shield-icon">
|
||||||
|
<el-icon :size="32"><CircleCheck /></el-icon>
|
||||||
|
</div>
|
||||||
|
<span>请完成安全验证</span>
|
||||||
|
</div>
|
||||||
|
<el-slider
|
||||||
|
v-model="sliderValue"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:show-tooltip="false"
|
||||||
|
@change="handleSliderChange"
|
||||||
|
class="custom-slider"
|
||||||
|
>
|
||||||
|
<template #button>
|
||||||
|
<el-icon>
|
||||||
|
<component :is="sliderValue >= 100 ? 'Select' : 'DArrowRight'" />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
</el-slider>
|
||||||
|
<div class="slider-text">
|
||||||
|
<el-icon><Right /></el-icon>
|
||||||
|
请向右滑动完成验证
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -193,7 +230,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { User, Lock, Phone, Message, Platform, Right, Key } from '@element-plus/icons-vue'
|
import { User, Lock, Phone, Message, Platform, Right, Key, DArrowRight, Select, CircleCheck } from '@element-plus/icons-vue'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { userApi } from '@/api/user'
|
import { userApi } from '@/api/user'
|
||||||
|
@ -310,30 +347,65 @@ const startEmailCooldown = () => {
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送手机验证码
|
// 滑块验证相关
|
||||||
const sendPhoneCode = async () => {
|
const sliderVisible = ref(false)
|
||||||
// if (phoneCooldown.value > 0 || !registerForm.phone) {
|
const sliderValue = ref(0)
|
||||||
// ElMessage.warning('请先输入手机号')
|
const verifiedSuccess = ref(false)
|
||||||
// return
|
const verifyType = ref<'phone' | 'email'>('phone')
|
||||||
// }
|
|
||||||
|
|
||||||
// 验证手机号格式
|
// 处理滑块验证
|
||||||
|
const handleSliderChange = (value: number) => {
|
||||||
|
if (value >= 100) {
|
||||||
|
verifiedSuccess.value = true
|
||||||
|
sliderVisible.value = false
|
||||||
|
sliderValue.value = 0
|
||||||
|
// 根据类型发送验证码
|
||||||
|
if (verifyType.value === 'phone') {
|
||||||
|
handleSendPhoneCode()
|
||||||
|
} else {
|
||||||
|
handleSendEmailCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改发送手机验证码方法
|
||||||
|
const sendPhoneCode = () => {
|
||||||
|
if (phoneCooldown.value > 0 || !registerForm.phone) return
|
||||||
if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
|
if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
|
||||||
ElMessage.warning('请输入正确的手机号')
|
ElMessage.warning('请输入正确的手机号')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
verifyType.value = 'phone'
|
||||||
|
sliderVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实际发送手机验证码
|
||||||
|
const handleSendPhoneCode = async () => {
|
||||||
|
try {
|
||||||
await userApi.sendVerifyPhoneCode(registerForm.phone)
|
await userApi.sendVerifyPhoneCode(registerForm.phone)
|
||||||
ElMessage.success('手机验证码已发送')
|
ElMessage.success('手机验证码已发送')
|
||||||
startPhoneCooldown()
|
startPhoneCooldown()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('发送失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送邮箱验证码
|
// 修改发送邮箱验证码方法
|
||||||
const sendEmailCode = async () => {
|
const sendEmailCode = () => {
|
||||||
if (emailCooldown.value > 0 || !registerForm.email) return
|
if (emailCooldown.value > 0 || !registerForm.email) return
|
||||||
|
verifyType.value = 'email'
|
||||||
|
sliderVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实际发送邮箱验证码
|
||||||
|
const handleSendEmailCode = async () => {
|
||||||
|
try {
|
||||||
await userApi.sendVerifyEmailCode(registerForm.email)
|
await userApi.sendVerifyEmailCode(registerForm.email)
|
||||||
ElMessage.success('邮箱验证码已发送')
|
ElMessage.success('邮箱验证码已发送')
|
||||||
startEmailCooldown()
|
startEmailCooldown()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('发送失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -640,4 +712,167 @@ const sendEmailCode = async () => {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 滑块验证码样式 */
|
||||||
|
.slider-container {
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-text {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider {
|
||||||
|
padding: 10px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 25px;
|
||||||
|
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__runway) {
|
||||||
|
height: 50px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__bar) {
|
||||||
|
height: 50px;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(64, 158, 255, 0.9),
|
||||||
|
rgba(103, 194, 58, 0.9)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 0 4px 12px rgba(103, 194, 58, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button-wrapper) {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
top: 0;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button) {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: none;
|
||||||
|
background: linear-gradient(135deg, #409eff, #36cfc9, #67c23a);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: gradient 3s ease infinite;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.4);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes gradient {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button:hover) {
|
||||||
|
transform: scale(1.08);
|
||||||
|
box-shadow: 0 6px 20px rgba(64, 158, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button .el-icon) {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #fff;
|
||||||
|
transition: all 0.3s;
|
||||||
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes success-pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.6);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 15px rgba(103, 194, 58, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(103, 194, 58, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__bar[style*="100%"]) {
|
||||||
|
background: linear-gradient(90deg, #67c23a, #95de64);
|
||||||
|
animation: success-pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slider-dialog) {
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slider-dialog .el-dialog__header) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px 24px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slider-dialog .el-dialog__header::before) {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, #409eff, #67c23a);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slider-dialog .el-dialog__title) {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slider-dialog .el-dialog__body) {
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shield-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
background: linear-gradient(135deg, #e6f3ff, #f0f9eb);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #409EFF;
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
||||||
|
animation: float 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-6px); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue