如何使用阿里云短信服务实现登录页面,手机验证码登录?

news/2024/4/29 5:23:58/文章来源:https://blog.csdn.net/XikYu/article/details/130345133

1:个人如何使用阿里云短信服务?

2022如何使用个人阿里云短信服务?_linxiMY的博客-CSDN博客添加完成之后,等待审核!一般2个小时就会出来审核结果了,这里我因为注册申请时填写规则有误,足足审核了7次才通过!点击进来以后,按照要求填写:因为一个账号只能选购一种,所以我这里就没法仔细演示了。然后点击添加即可,一把2-3个小时就可以申请出结果了。ConstantPropertiesUtils 工具类。点击登录阿里云短信服务控制台--->点击。3:创建一个SpringBoot 启动类。将下面的服务地址改为自己对应的地址。AccessKey 保存下来!4:添加一个配置工具类类。开启启动类调接口测试。https://blog.csdn.net/XikYu/article/details/127617049?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168232320316800192242795%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168232320316800192242795&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-127617049-null-null.blog_rank_default&utm_term=%E9%98%BF%E9%87%8C%E4%BA%91&spm=1018.2226.3001.4450

2:直接上代码

2.1:建表SQL

CREATE TABLE `sys_user`
(`id`          varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'id',`username`    varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',`password`    varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',`nickname`    varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称',`email`       varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',`phone`       varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',`address`     varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`avatarUrl`   varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

2.2:后端代码

引入pom.xml 依 赖:

<dependencies><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId></dependency>
</dependencies>

yml 文件:

server:port: 9999
spring:application:name: demo-enddatasource:druid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/数据库?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: rootredis:host: ip地址port: 6379lettuce:pool:max-active: 8 # ????max-idle: 8 # ??????min-idle: 0 # ??????max-wait: 100ms # ??????
mybatis:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:mapper/*.xmltype-aliases-package: com.xi.demoend.entity

application.properties:


aliyun.sms.regionId=default
aliyun.sms.accessKeyId=LTAI5tBP98NtTK3gC5mgRQBz
aliyun.sms.secret=gH1w8F0jEWVXSiClmAAnowRw93YTFg

common公共包下的类:

 全局统一返回结果:

package com.xialj.demoend.common;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/*** @Author * @Date Created in  2023/2/23 17:25* @DESCRIPTION:  全局统一返回结果* @Version V1.0*/
@Data
@ApiModel(value = "全局统一返回结果")
@SuppressWarnings("all")
public class Result<T> {@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private T data;private Long total;public Result(){}protected static <T> Result<T> build(T data) {Result<T> result = new Result<T>();if (data != null)result.setData(data);return result;}public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {Result<T> result = build(body);result.setCode(resultCodeEnum.getCode());result.setMessage(resultCodeEnum.getMessage());return result;}public static <T> Result<T> build(Integer code, String message) {Result<T> result = build(null);result.setCode(code);result.setMessage(message);return result;}public static<T> Result<T> ok(){return Result.ok(null);}/*** 操作成功* @param data* @param <T>* @return*/public static<T> Result<T> ok(T data){Result<T> result = build(data);return build(data, ResultCodeEnum.SUCCESS);}public static<T> Result<T> fail(){return Result.fail(null);}/*** 操作失败* @param data* @param <T>* @return*/public static<T> Result<T> fail(T data){Result<T> result = build(data);return build(data, ResultCodeEnum.FAIL);}public Result<T> message(String msg){this.setMessage(msg);return this;}public Result<T> code(Integer code){this.setCode(code);return this;}public boolean isOk() {if(this.getCode().intValue() == ResultCodeEnum.SUCCESS.getCode().intValue()) {return true;}return false;}
}

 统一返回结果状态信息类:

package com.xialj.demoend.common;import lombok.Getter;/*** @Author * @Date Created in  2023/2/23 17:25* @DESCRIPTION:  统一返回结果状态信息类* @Version V1.0*/
@Getter
@SuppressWarnings("all")
public enum ResultCodeEnum {SUCCESS(200,"成功"),FAIL(201, "失败"),PARAM_ERROR( 202, "参数不正确"),SERVICE_ERROR(203, "服务异常"),DATA_ERROR(204, "数据异常"),DATA_UPDATE_ERROR(205, "数据版本异常"),LOGIN_AUTH(208, "未登陆"),PERMISSION(209, "没有权限"),CODE_ERROR(210, "验证码错误"),
//    LOGIN_MOBLE_ERROR(211, "账号不正确"),LOGIN_DISABLED_ERROR(212, "改用户已被禁用"),REGISTER_MOBLE_ERROR(213, "手机号码格式不正确"),REGISTER_MOBLE_ERROR_NULL(214, "手机号码为空"),LOGIN_AURH(214, "需要登录"),LOGIN_ACL(215, "没有权限"),URL_ENCODE_ERROR( 216, "URL编码失败"),ILLEGAL_CALLBACK_REQUEST_ERROR( 217, "非法回调请求"),FETCH_ACCESSTOKEN_FAILD( 218, "获取accessToken失败"),FETCH_USERINFO_ERROR( 219, "获取用户信息失败"),//LOGIN_ERROR( 23005, "登录失败"),PAY_RUN(220, "支付中"),CANCEL_ORDER_FAIL(225, "取消订单失败"),CANCEL_ORDER_NO(225, "不能取消预约"),HOSCODE_EXIST(230, "医院编号已经存在"),NUMBER_NO(240, "可预约号不足"),TIME_NO(250, "当前时间不可以预约"),SIGN_ERROR(300, "签名错误"),HOSPITAL_OPEN(310, "医院未开通,暂时不能访问"),HOSPITAL_LOCK(320, "医院被锁定,暂时不能访问"),HOSPITAL_LOCKKEY(330,"医院对应key不一致");private Integer code;private String message;private ResultCodeEnum(Integer code, String message) {this.code = code;this.message = message;}
}

untils包下的工具类:

ConstantPropertiesUtils 用来读取 application.properties文件中的配置

package com.xialj.demoend.utils;import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/*** @Author * @Date Created in  2023/2/23 17:25* @DESCRIPTION:* @Version V1.0*/
@Component
public class ConstantPropertiesUtils implements InitializingBean {//InitializingBean  初始化bean  让spring 容器一初始化就加载@Value("${aliyun.sms.regionId}")private String regionId;@Value("${aliyun.sms.accessKeyId}")private String accessKeyId;@Value("${aliyun.sms.secret}")private String secret;public static String REGION_Id;public static String ACCESS_KEY_ID;public static String SECRECT;@Overridepublic void afterPropertiesSet() throws Exception {REGION_Id=regionId;ACCESS_KEY_ID=accessKeyId;SECRECT=secret;}
}

 RandomUtil 工具类用来生成6位验证码:

package com.xialj.demoend.utils;import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
/*** @Author * @Date Created in  2023/2/23 17:25* @DESCRIPTION:* @Version V1.0*/
@SuppressWarnings("ALL")
public class RandomUtil {private static final Random random = new Random();private static final DecimalFormat fourdf = new DecimalFormat("0000");private static final DecimalFormat sixdf = new DecimalFormat("000000");public static String getFourBitRandom() {return fourdf.format(random.nextInt(10000));}public static String getSixBitRandom() {return sixdf.format(random.nextInt(1000000));}/*** 给定数组,抽取n个数据* @param list* @param n* @return*/public static ArrayList getRandom(List list, int n) {Random random = new Random();HashMap<Object, Object> hashMap = new HashMap<Object, Object>();// 生成随机数字并存入HashMapfor (int i = 0; i < list.size(); i++) {int number = random.nextInt(100) + 1;hashMap.put(number, i);}// 从HashMap导入数组Object[] robjs = hashMap.values().toArray();ArrayList r = new ArrayList();// 遍历数组并打印数据for (int i = 0; i < n; i++) {r.add(list.get((int) robjs[i]));System.out.print(list.get((int) robjs[i]) + "\t");}System.out.print("\n");return r;}
}

User实体类:(实现了简单的手机验证码登录+email邮箱登录)

package com.xialj.demoend.entity;import com.alibaba.druid.sql.visitor.functions.Insert;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;/*** @Author * @Date Created in  2023/2/24 13:07* @DESCRIPTION:   User 实体类* @Version V1.0*/@Data
public class User extends PageQuery implements Serializable {private static final long serialVersionUID = 1L;/*** id*/private String id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 昵称*/private String nickname;/*** 邮箱*/@ApiModelProperty(value = "联系人邮箱")@NotBlank(message = "邮箱不能为空", groups = {Insert.class})@NotNull(message = "邮箱不能为空", groups = {Insert.class})@Pattern(regexp = ".+@.+\\.com$", message = "Email格式不正确")private String email;/*** 电话*/@ApiModelProperty(value = "联系人电话")@NotBlank(message = "手机号码不能为空", groups = {Insert.class})@NotNull(message = "手机号不能为空", groups = {Insert.class})@Length(min = 11, max = 11, message = "手机号只能为11位")@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")private String phone;/*** 地址*/private String address;/*** 验证码*/private Integer code;/*** 创建时间*/@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")private Date createTime;/*** 头像*/private String avatarUrl;
}

MsmApiController:用来实现发送验证码的接口

package com.xialj.demoend.controller;import cn.hutool.extra.mail.MailUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xialj.demoend.common.Result;
import com.xialj.demoend.common.ResultCodeEnum;
import com.xialj.demoend.common.paramCommon;
import com.xialj.demoend.entity.User;
import com.xialj.demoend.service.MsmService;
import com.xialj.demoend.utils.JsonSerializer;
import com.xialj.demoend.utils.RandomUtil;
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.mail.MessagingException;
import javax.validation.constraints.Email;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/*** @Author xlj* @Date Created in  2023/2/23 17:25* @DESCRIPTION:  主要是为了实现邮箱发送验证码,以及手机发送验证码登录* @Version V1.0*/
@RestController
@RequestMapping("/api/msm")
@SuppressWarnings("ALL")
@Slf4j
@CrossOrigin
public class MsmApiController {@Autowiredprivate MsmService msmService;@Autowiredprivate RedisTemplate<String,String> redisTemplate;//将手机验证码存放到redis  中,并设置过期时间@ApiOperation(value = "发送手机验证码")@PostMapping("sendPhoneCode")public Result sendCode(@RequestBody(required = true) @Validated User user){String phoneNum = user.getPhone();// phone  作为redis  中的key,  code  作为redis  中的value值String code = redisTemplate.opsForValue().get(phoneNum);if (!StringUtils.isEmpty(code)) {//这里为什么从redis 拿取 验证码  我理解的是 ,因为redis 验证码我们给他设置了时间限制,所以在规定时间内,验证码//可以多次使用,一旦时间到期之后,我们就需要从新生成6位数的验证码了。return Result.ok(code);}//生成六位验证码code = RandomUtil.getSixBitRandom();//调用service 方法,整合阿里云短信服务进行发送Boolean isSend = msmService.send(phoneNum,code);//返回的Boolean 值进行判断 ,如果发送信息成功,即存入到redis 中,如果没有则提示验证码发送失败if (isSend) {//放到redis中规定时间内有效redisTemplate.opsForValue().set(phoneNum,code,1, TimeUnit.MINUTES);redisTemplate.setDefaultSerializer(new JsonSerializer<>());
//            redisTemplate.opsForValue().set(phoneNum,code);String codeMessage = redisTemplate.opsForValue().get(phoneNum);Set<String> allKeys = redisTemplate.keys("*");for (String key : allKeys) {System.out.println(key);System.out.println(key.getClass().getName());}log.info("当前存储的验证码为:{}",codeMessage);return Result.ok(code);} else {return Result.fail().message(paramCommon.FAIL_MESSAGE);}}
}

MsmServiceImpl实现类:

package com.hospital.service.impl;import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.hospital.service.MsmService;
import com.hospital.utils.ConstantPropertiesUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.HashMap;
import java.util.Map;@Service
public class MsmServiceImpl implements MsmService {//根据手机号,存入验证码@Overridepublic Boolean send(String phone, String code) {//判断手机号是否为空if (StringUtils.isEmpty(phone)) {return false;}//整合阿里云的短信服务DefaultProfile profile = DefaultProfile.getProfile(ConstantPropertiesUtils.REGION_Id,ConstantPropertiesUtils.ACCESS_KEY_ID,ConstantPropertiesUtils.SECRECT);IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();//request.setProtocol(ProtocolType.HTTPS);request.setMethod(MethodType.POST);request.setDomain("dysmsapi.aliyuncs.com");request.setVersion("2017-05-25");request.setAction("SendSms");
//        request.setSysAction();//手机号request.putQueryParameter("PhoneNumbers", phone);//签名名称request.putQueryParameter("SignName", "自己申请的短信签名");//模板coderequest.putQueryParameter("TemplateCode", "短信模板CODE");//验证码  使用json格式   {"code":"123456"}Map<String,Object> param = new HashMap();param.put("code",code);request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));//调用方法进行短信发送try {CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());return response.getHttpResponse().isSuccess();} catch (ServerException e) {e.printStackTrace();} catch (ClientException e) {e.printStackTrace();}return false;}
}

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 {@Autowiredprivate 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-limitclearableprefix-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-limitprefix-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 clearableprefix-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-limitprefix-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-whitespaceclearInterval(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>

 页面效果图:

手机登录

 邮箱登录

 短信:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103071.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest 学习三(前置后置操作)

pytest测试框架_pytest框架-CSDN博客 一、常用的操作 一、setup/teardown 每个用例之前、之后执行 二、setup_class/teardown_class 在每个类之前、之后执行一次 二、pytest.fixture 设置前置后置操作范围 pytest.fixture(scope"",params,autouse,ids,name) 其中 sc…

JavaScript模块化开发

目录&#xff1a; 1 认识模块化开发 2 CommonJS和Node 3 require函数解析 4 AMD和CMD&#xff08;了解&#xff09; 5 ESModule用法详解 6 ESModule运行原理 模块化不是两个不同的js文件直接导入到某个页面中的&#xff0c;因为这两个文件只要有相同的变量或函数&#xf…

R基础函数概览(一)

rep 函数形式&#xff1a;rep(x, time , length , each ,) 参数说明&#xff1a; x&#xff1a;代表的是你要进行复制的对象&#xff0c;可以是一个向量或者是一个因子。 times&#xff1a;代表的是复制的次数&#xff0c;只能为正数。负数以及NA值都会为错误值。复制是指的…

云原生(docker+k8s+阿里云)-Docker

Gitee-Kubernetes学习 kubectl备忘清单 k8s官方文档-task [云原生-kubectl命令详解] ingress详解 ingress官方文档 云原生-语雀-架构师第一课 从Docker到Kubernetes进阶-社区 云计算学习路线-阿里云大学 如上图&#xff0c;服务器有公网ip和私网ip&#xff0c;公网ip是外部访问…

Ubuntu20.04使用多卡训练HyperNetwork模型和LoRA模型全流程及疑难问题解决方案

目录 一. LoRA模型多卡训练1.1 安装xformer等库1.2 设置路径1.3 多卡训练 二. LoRA模型多卡训练疑难报错解决方案多卡训练报错 软硬件配置&#xff1a; CPU: AMD 5800 8core 16Thread GPU: NVIDIA RTX 3090 *1 NVIDIA TITAN RTX *1 OS: Ubuntu20.04 一. LoRA模型多卡训练 1.1 …

一篇终结synchronized

一&#xff1a;基本原理 Java对象在内存中由两部分组成 &#xff1a; 1 是成员变量 2 是对象头&#xff0c;以32位虚拟机介绍&#xff1a;此时对象头是64位&#xff0c;即8字节 其中32个字节代表 mark word 另外32个字节代表klass word分别是什么意思呢&#xff1f; 1 klass …

写题总结1

先把自己写完的总结一下&#xff1a; 题目一&#xff1a; 猫儿园的告示牌上贴着 ab 大小的矩形广告纸。猫猫对广告不感兴趣&#xff0c;她想知道能否用 cd 的矩形白纸完全覆盖这个广告。猫猫可以对白纸进行平移、旋转&#xff0c;但不能折叠或撕开等。如果可以完全覆盖输出 YE…

滴水逆向3期笔记与作业——01汇编

防止OneNote丢失。 海哥牛逼。 01汇编笔记 01进制进制定义10-2进制转换八进制 02数据宽度/逻辑运算数据宽度与存储逻辑运算计算机做加法的本质作业 03通用寄存器_内存读写通用寄存器表通用寄存器图内存读写计算机操作系统位数意义 04内存地址_堆栈寻址公式PUSH指令POP指令作业 …

【IAR工程】STM8S基于ST标准库读取DHT11数据

【IAR工程】STM8S基于ST标准库读取DHT11数据 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取&#xff0c;请多多尊重和支持原创!&#x1f341;对于文中所提供的相关资源链接将作不定期更换。&…

4月有8本SCIE期刊被剔除(附MDPI/Frontiers/Hindawi最新在检期刊)

2023年4月SCI、SSCI期刊目录更新 2023年4月18日&#xff0c;科睿唯安更新了WOS期刊目录&#xff0c;继上次3月WOS期刊目录剔除50本SCIE&SSCI期刊之后&#xff0c;此次4月更新又有8本SCIE期刊发生变动&#xff0c;其中有4本期刊被踢出SCIE数据库&#xff0c;4本期刊更改了名…

流程图拖拽视觉编程--概述

一般的机器视觉平台采用纯代码的编程方式&#xff0c;如opencv、halcon&#xff0c;使用门槛高、难度大、定制性强、开发周期长&#xff0c;因此迫切需要一个低代码开发的视觉应用平台。AOI缺陷检测的对象往往缺陷种类多&#xff0c;将常用的图像处理算子封装成图形节点,如抓直…

Android 系统架构大图

android的系统架构和其操作系统一样&#xff0c;采用了分层的架构。从架构图看&#xff0c;android分为四个层&#xff0c;从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux核心层。 1.应用程序 Android会同一系列核心应用程序包一起发布&#xff0c;该应用…

确定因果随机森林的树木数量 the number of trees

前言 推断因果性和分析异质性是统计学家在处理混杂任务中的圣杯。传统且主流的方法有:倾向性评分、分层分享、比例风险模型等。新的方法也有很多,代表就是:因果随机森林。这种算法,浅看难度一般,深入探索发现坑还是很多的。这篇博客不对算法做深入探讨,仅仅是我在阅读文…

Nautilus Chain :基于模块化架构的Layer3正在走向成熟

Nautilus Chain 是一个基于 Eclipse 和 Celestia 构建的模块化 Layer3 链。作为定位在 Layer0 的链基建概念&#xff0c;Eclipse 和 Celestia 为面向未来的区块链扩容技术提供了一套开发工具和基础框架。尽管这种前沿技术过去一直处于概念验证阶段&#xff0c;尚未推出适用于大…

Java并发(三)----创建线程的三种方式及查看进程线程

一、直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐 Thread t1 new Thread("t1") {Override// run 方法内实现…

手把手教你PXE高效网络装机、Kickstart无人值守安装(详细版)

目录 一、部署PXE远程安装服务1.1PXE定义1.2PXE服务优点1.3搭建网络体系前提条件1.4 搭建PXE远程安装服务器 二. 实验2.1 服务器操作2.2 安装启动TFTP服务并修改TFTP服务的配置文件2.3 安装并启用DHCP服务2.4 准备linux内核&#xff0c;初始化镜像文件2.5 准备PXE引导程序2.6 安…

22、Tweak原理及部分逆向防护

一、Tweak原理 1.1 Tweak产物.dylib 执行make命令时,在 .theos的隐藏目录中,编译出obj/debug目录,包含 arm64、arm64e两种架构,同时生成readbadges.dylib动态库 在arm64、arm64e目录下,有各自架构的readbadges.dylib,而debug目录下的readbadges.dylib,是一个胖二进制文件 fi…

【Java-01】深入浅出匿名对象 , 继承 , 抽象类

主要内容 面向对象回顾 匿名对象介绍 面向对象特征 - 继承 抽象类的使用 模板设计模式 1 面向对象回顾 面向对象的核心思想是什么 ? 用代码来模拟现实生活中的事物 , 比如学生类表示学生事物 , 对象表示的就是具体的学生 , 有了类就可以描述万千世界所有的事物了 现有的…

看完这篇文章你就彻底懂啦{保姆级讲解}-----(LeetCode刷题142环形链表II) 2023.4.24

目录 前言算法题&#xff08;LeetCode刷题142环形链表II&#xff09;—&#xff08;保姆级别讲解&#xff09;分析题目&#xff1a;算法思想环形链表II代码&#xff1a;补充 结束语 前言 本文章一部分内容参考于《代码随想录》----如有侵权请联系作者删除即可&#xff0c;撰写…

前端食堂技术周刊第 80 期:Vite 4.3、Node.js 20、TS 5.1 Beta、Windi CSS 即将落幕

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;东坡肉 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 本期摘要 Vite 4.3Node.js 20TypeScript 5.1 BetaWindi CSS 即将落幕Pretty TypeScri…