LoginController登录接口:(通过@Validated 对手机号,邮箱等参数进行校验)
package com.xialj.demoend.controller; import cn.hutool.core.util.StrUtil; import com.xialj.demoend.common.Result; import com.xialj.demoend.entity.User; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.math.BigInteger; import java.util.Optional; import java.util.function.Supplier; /** * @Author * @Date Created in 2023/3/18 14:30 * @DESCRIPTION: * @Version V1.0 */ @RestController @RequestMapping("/user/login") @Slf4j @CrossOrigin public class LoginController { @Autowired private RedisTemplate<String,String> redisTemplate; @ApiOperation("手机验证码登录接口") @PostMapping("/phoneCode") @SuppressWarnings("all") public Result phoneCodeLogin(@RequestBody @Validated User user) throws Throwable { Optional.ofNullable(user.getPhone()).orElseThrow((Supplier<Throwable>) () -> new RuntimeException("手机号码为null")); String phone = user.getPhone(); log.info("当前获取的手机号为:{}",phone); //从redis中获取手机验证码 String userPhoneKey = redisTemplate.opsForValue().get(phone); if (StringUtils.isEmpty(userPhoneKey)) { return Result.fail("手机验证码有误"); } if (!userPhoneKey.equals(String.valueOf(user.getCode()))) { return Result.fail("手机验证码有误"); } return Result.ok("登录成功"); } }
前端vue代码:
<template> <div class="cont1" id="mainContainer" v-title data-title="若梦管理系统"> <p class="tip">Click on button in image container</p> <div class="cont" style="border-radius: 15px"> <div class="form sign-in" style="line-height: 50px;"> <h2 style="margin-top: 1px">后台管理系统</h2> <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px"> <el-form-item label="手机号" prop="phone" style="width: 50%;margin-left: 110px;margin-top: 80px"> <el-input v-model.number="formData.phone" placeholder="请输入手机号" :maxlength="11" show-word-limit clearable prefix-icon='el-icon-mobile-phone' :style="{width: '200px'}"></el-input> </el-form-item> <el-form-item label="验证码" prop="yzm" style="width: 50%;margin-left: 110px;margin-top: 50px"> <el-input v-model.number="formData.yzm" :maxlength="6" :minlength="6" clearable show-word-limit prefix-icon='el-icon-chat-round' oninput="value=value.replace(/[^0-9.]/g,'')" :style="{width: '200px'}"> <template #append > <el-button class="test" style="width: 5px; margin-right: 10px; text-align: center; background-color: inherit;" type="danger" :disabled="remainingTime > 0" @click="startTimer"> {{ remainingTime > 0 ? '('+remainingTime+'s)' : '获取' }} </el-button> </template> </el-input> </el-form-item> <el-form-item> <el-button type="primary" :loading="loading" @click="submitForm('/manage/home')" class="submit">登录 </el-button> </el-form-item> </el-form> </div> <div class="sub-cont"> <div class="img"> <div class="img__text m--up"> <h2>第一次来?</h2> <p>注册并发现大量的新机会!</p> </div> <div class="img__text m--in"> <h2>加入我们 ?</h2> <p>如果你已经有一个账户,只需登录即可 </p> <span>We've missed you!</span> </div> <div class="img__btn"> <span class="m--up">邮箱登录</span> <span class="m--in">手机登录</span> </div> </div> <div class="form sign-up" style="line-height: 50px"> <h2>Time to feel like home</h2> <el-form ref="elForm2" :model="formData" :rules="rules2" size="medium" label-width="100px"> <el-form-item label="电子邮箱" prop="email" style="width: 50%;margin-left: 110px;margin-top: 80px"> <el-input v-model="formData.email" placeholder="请输入邮箱" show-word-limit clearable prefix-icon='el-icon-message' :style="{width: '200px'}"></el-input> </el-form-item> <el-form-item label="验证码" prop="yzm2" style="width: 50%;margin-left: 110px;margin-top: 50px"> <el-input v-model.number="formData.yzm2" :maxlength="6" :minlength="6" clearable show-word-limit prefix-icon='el-icon-chat-round' oninput="value=value.replace(/[^0-9.]/g,'')" :style="{width: '200px'}"> <template #append > <el-button class="test" style="width: 5px; margin-right: 10px; text-align: center; background-color: inherit;" type="danger" :disabled="remainingTime > 0" @click="startTimer2"> {{ remainingTime > 0 ? '('+remainingTime+'s)' : '获取' }} </el-button> </template> </el-input> </el-form-item> <el-form-item> <el-button type="primary" :loading="loading2" @click="submitForm2('/manage/home')" class="submit">登录 </el-button> </el-form-item> </el-form> </div> </div> </div> </div> </template> <script> import {sendPhoneCode, sendEmailCode,phoneLogin,emailLogin} from "@/api/login.js"; export default { name: 'login', props: { msg: String }, data() { return { remainingTime: 0, loading: false, loading2:false, timer: null, formData: { pwd: '', phone: '', email: '', yzm: '', yzm2: '' }, rules: { phone: [{ required: true, message: '请输入手机号', trigger: 'change' }, { pattern: /^1(3|4|5|7|8|9)\d{9}$/, message: '手机号格式错误', trigger: 'blur' }], yzm: [ {required: true, message: '验证码不能为空', trigger: 'change'}, // {type: 'number', message: '验证码必须为数字', trigger: ['blur', 'change']} ], }, rules2: { email: [ {required: true, message: '请输入邮箱地址', trigger: 'change'}, {type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']} ], yzm2: [ {required: true, message: '验证码不能为空', trigger: 'change'}, // {type: 'number', message: '验证码必须为数字', trigger: ['blur', 'change']} ] }, } }, methods: { //手机号登录 submitForm(path) { this.$refs['elForm'].validate(valid => { if (!valid) return // 显示loading 加载效果 this.loading = true; const user = { phone:this.formData.phone, code: this.formData.yzm } this.timer = setTimeout(() => { phoneLogin(user).then( res=>{ if (res.code == 200){ this.$message({ type: 'success', message: '登录成功!' }) // 处理业务逻辑 以及发送请求 this.$router.push(path) }else { this.$message({ type: 'error', message: '登录失败!' }); } }).finally(( this.loading = false )); }, 1000); }) }, //邮箱登录 submitForm2(path) { this.$refs['elForm2'].validate(valid => { if (!valid) return // TODO 提交表单 // 显示loading 加载效果 this.loading2 = true; const user = { email:this.formData.email, code: this.formData.yzm2 } this.timer = setTimeout(() => { // 处理业务逻辑 以及发送请求 emailLogin(user).then(res=>{ console.log(res) if (res.code == 200){ this.$message({ type: 'success', message: '登录成功!' }) // 处理业务逻辑 以及发送请求 this.$router.push(path) }else { this.$message({ type:'error', message: '登录失败!' }) } }).finally(( this.loading2 = false )); }, 1000); }) }, //发送手机验证码 sendCode() { // TODO 提交表单 let user ={phone:this.formData.phone} console.log(user) sendPhoneCode(user).then(res => { if (res.code == 200) { this.$message({ type: 'success', message: '验证码发送成功!' }) } else { this.$message({ type: 'warning', message: '验证码发送失败!' }); } }) }, startTimer() { this.$refs['elForm'].validateField('phone', valid => { if (valid) return // TODO 提交表单 this.remainingTime = 60; this.sendCode() const timer = setInterval(() => { this.remainingTime--; if (this.remainingTime === 0) { clearInterval(timer); } }, 1000); }) }, startTimer2() { this.$refs['elForm2'].validateField('email', valid => { if (valid) return // TODO 提交表单 this.remainingTime = 60; this.sendCodeTwo() const timer = setInterval(() => { this.remainingTime--; if (this.remainingTime === 0) { clearInterval(timer); } }, 1000); }) }, //发送邮箱验证码 sendCodeTwo() { // TODO 提交表单 let user ={email:this.formData.email} console.log(user) sendEmailCode(user).then(res => { if (res.code == 200) { this.$message({ type: 'success', message: '验证码发送成功!' }) } else { this.$message({ type: 'warning', message: '验证码发送失败!' }); } }) }, }, mounted() { document.querySelector('.img__btn').addEventListener('click', function () { const contEl = document.querySelector('.cont'); contEl.classList.toggle('s--signup'); contEl.addEventListener('transitionend', function () { if (contEl.classList.contains('s--signup')) { contEl.querySelectorAll('input').forEach(inputEl => inputEl.value = ''); } }, {once: true}); }); }, beforeDestroy() { // eslint-disable-next-line no-irregular-whitespace clearInterval(this.timer); // 清除定时器 this.timer = null; }, } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> *, *:before, *:after { box-sizing: border-box; margin: 0; padding: 0; } input, button { border: none; outline: none; background: none; font-family: "Open Sans", Helvetica, Arial, sans-serif; } .tip { font-size: 20px; margin: 40px auto 50px; text-align: center; } .cont1::before { content: ''; position: fixed; background-color: #ededed; width: 100%; height: 100%; left: 0; top: 0; background-image: linear-gradient(to bottom right ,#d3dae9,#14c2c2) } .cont { overflow: hidden; position: relative; width: 900px; height: 550px; margin: 0 auto 100px; background: #fff; } .form { position: relative; width: 640px; height: 100%; transition: transform 1.2s ease-in-out; padding: 50px 30px 0; } .sub-cont { overflow: hidden; position: absolute; left: 640px; top: 0; width: 900px; height: 100%; padding-left: 260px; background: #fff; transition: transform 1.2s ease-in-out; } .cont.s--signup .sub-cont { transform: translate3d(-640px, 0, 0); } button { display: block; margin: 0 auto; width: 260px; height: 36px; border-radius: 30px; color: #fff; font-size: 15px; cursor: pointer; } .img { overflow: hidden; z-index: 2; position: absolute; left: 0; top: 0; width: 260px; height: 100%; padding-top: 360px; } .img:before { content: ""; position: absolute; right: 0; top: 0; width: 900px; height: 100%; background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/sections-3.jpg"); background-size: cover; transition: transform 1.2s ease-in-out; } .img:after { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); } .cont.s--signup .img:before { transform: translate3d(640px, 0, 0); } .img__text { z-index: 2; position: absolute; left: 0; top: 50px; width: 100%; padding: 0 20px; text-align: center; color: #fff; transition: transform 1.2s ease-in-out; } .img__text h2 { margin-bottom: 10px; font-weight: normal; } .img__text p { font-size: 14px; line-height: 1.5; } .cont.s--signup .img__text.m--up { transform: translateX(520px); } .img__text.m--in { transform: translateX(-520px); } .cont.s--signup .img__text.m--in { transform: translateX(0); } .img__btn { overflow: hidden; z-index: 2; position: relative; width: 100px; height: 36px; margin: 0 auto; background: transparent; color: #fff; text-transform: uppercase; font-size: 15px; cursor: pointer; } .img__btn:after { content: ""; z-index: 2; position: absolute; left: 0; top: 0; width: 100%; height: 100%; border: 2px solid #fff; border-radius: 30px; } .img__btn span { position: absolute; left: 0; top: 0; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; transition: transform 1.2s; } .img__btn span.m--in { transform: translateY(-72px); } .cont.s--signup .img__btn span.m--in { transform: translateY(0); } .cont.s--signup .img__btn span.m--up { transform: translateY(72px); } h2 { width: 100%; font-size: 26px; text-align: center; } label { display: block; width: 260px; margin: 25px auto 0; text-align: center; } label span { font-size: 12px; color: #cfcfcf; text-transform: uppercase; } input { display: block; width: 100%; margin-top: 5px; padding-bottom: 5px; font-size: 16px; border-bottom: 1px solid rgba(0, 0, 0, 0.4); text-align: center; } .forgot-pass { margin-top: 15px; text-align: center; font-size: 12px; color: #cfcfcf; } .submit { margin-top: 30px; margin-left: 50px; margin-bottom: 10px; background: #d4af7a; /*text-transform: uppercase;*/ } .style2 { margin-top: 10px; margin-right: 100px; margin-bottom: 10px; background: darkgrey; text-transform: uppercase; } .fb-btn { border: 2px solid #d3dae9; color: #8fa1c7; } .fb-btn span { font-weight: bold; color: #455a81; } .sign-in { transition-timing-function: ease-out; } .cont.s--signup .sign-in { transition-timing-function: ease-in-out; transition-duration: 1.2s; transform: translate3d(640px, 0, 0); } .sign-up { transform: translate3d(-900px, 0, 0); } .cont.s--signup .sign-up { transform: translate3d(0, 0, 0); } .icon-link { position: absolute; left: 5px; bottom: 5px; width: 32px; } .icon-link img { width: 100%; vertical-align: top; } .icon-link--twitter { left: auto; right: 5px; } .test[disabled]:not(.is-loading) { opacity: 0.5; cursor: not-allowed; width: 5px; margin-right: 10px; text-align: center; } </style>
页面效果图:
手机登录
邮箱登录
短信: