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

news2024/11/24 15:40:50

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-end
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/数据库?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: root
  redis:
    host: ip地址
    port: 6379
    lettuce:
      pool:
        max-active: 8 # ????
        max-idle: 8 # ??????
        min-idle: 0 # ??????
        max-wait: 100ms # ??????
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
  type-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;

    @Override
    public 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>();

// 生成随机数字并存入HashMap
        for (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 {
    @Autowired
    private MsmService msmService;

    @Autowired
    private 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 {
    //根据手机号,存入验证码
    @Override
    public 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", "自己申请的短信签名");
        //模板code
        request.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 {
   @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>

 页面效果图:

手机登录

 邮箱登录

 短信:

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis的缓存、逆向工程、以及分页插件的使用

1、MyBatis的缓存 1.1、MyBatis的一级缓存 一级缓存是SqlSession级别的&#xff0c;通过同一个SqlSession查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就会从缓存中直接获取&#xff0c;不会从数据库重新访问 使一级缓存失效的四种情况&#xff1a; 不同的…

Spring简介

二、Spring 1、Spring简介 1.1、Spring概述 官网地址&#xff1a;https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Jav…

人工大猩猩部队优化器:一种新的面向全局优化问题的自然启发元启发式算法(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 元启发式在解决优化问题方面发挥着关键作用&#xff0c;其中大多数都受到自然界中自然生物集体智慧的启发。本文提出了一种新的…

流程图的简单操作

流程图是一种对过程进行梳理的工具&#xff0c;利用简单的图形符号来将组织步骤连接起来&#xff0c;从而形成完整信息流转图形。该图直观地描述一个工作过程的具体步骤&#xff0c;对准确了解事情是如何进行的&#xff0c;以及决定应如何改进过程有指导性意义。 流程图如何进行…

SwiftUI 中 TabView 如何原生使用类 UIPageView 的翻页样式?

功能需求 我们知道 TabView 是 SwiftUI 中非常好用的布局组织容器,它可以分类组织视图并依次展示给用户。 从 SwiftUI 2.0 开始(iOS 14.0+),TabView 除了常规的以标签(Tab Label)样式显示外,还可以用类似 UIPageView 的样式分页原生显示视图,显得更加简洁: 如上图所…

远程控制软件有哪些

在当今的数字时代&#xff0c;远程访问软件已成为各种规模企业的必备工具。 它允许员工在任何地方工作并远程访问他们的工作计算机和数据。 当今市场上三种最流行的远程访问软件解决方案是 TeamViewer、Splashtop 和 Microsoft Remote Desktop。 在本文中&#xff0c;我们将深入…

【MYSQL】MYSQL数据库命令大全

文章目录 &#x1f337; 1. root 用户登录&#x1f337; 2. 数据库的操作&#x1f337; 3. 常用数据类型&#x1f337; 4. 表的操作⭐ 4.1 创建表&#x1f341; 约束类型 ⭐ 4.2 插入⭐ 4.3 查询&#x1f341; 去重&#xff1a;DISTINCT&#x1f341; 排序&#xff1a;ORDER BY…

Mysql设置表只存储一段时间的数据

使用MySQL的事件调度器&#xff08;Event Scheduler&#xff09;来定期删除表中的数据。 假设你要删除的表是mytable&#xff0c;并且表中有一个名为created_at的日期时间类型的列&#xff0c;存储了每条记录的创建时间。你可以通过以下步骤设置表只存储30天的数据&#xff1a…

CentOS7.x docker安装Oracle11g并使用navicat连接

1.拉取Oracle11g镜像 #因为国内访问docker官方镜像源速度很慢,所以这里使用阿里的 docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g#名字太长,重新打标签 docker tag registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g:latest helowin/oracle_11g 2.启…

采用海明窗,设计FIR滤波器

clc; % 清空命令行窗口 clear; %清空工作区 close all;% fir1函数使用方法 b fir1(48,[0.35 0.65]); freqz(b,1,512)figure b1 fir1(11,0.2); plot(20*log(abs(fft(b1)))/log(10))% E4_1_fir1.m文件的源码 % % 采用海明窗&#xff0c;分别设计长度为41&#xff08;阶数为40…

python基基基基基基础知识

python 基础入门 一、python介绍和安装 1. python语言的特点 语法简洁类库丰富跨平台可扩展源码开放 2. python版本 官方版本 只包含python的解释程序和一些标准库 https://www.python.org/downloads/&#xff0c;安装后打开终端输入python&#xff0c;显示python版本即为安…

手推FlinkML2.2(二)

FeatureHasher&#xff08;特征哈希器&#xff09;是一种用于处理高维分类特征&#xff08;如文本或类别特征&#xff09;的方法。它将特征转换为固定长度的数值向量&#xff0c;以便在机器学习模型中使用。特征哈希通过哈希函数将特征映射到较低维度的向量空间&#xff0c;可以…

【Linux网络】PXE高效批量网络装机

PEX高效批量网络装机 一、部署PXE远程安装服务1.1PXE的优点1.2搭建PXE网络体系的前提条件 二、实现Kincksatrt无人值守安装2.1实验思路&#xff0c;2.2实验&#xff1a;无人值守远程安装2.2.1实现 Kickstart 无人值守安装 一、部署PXE远程安装服务 PXE&#xff08;预启动执行环…

Apple 的 plist 编辑器入门指南:基础操作与高级功能详解

PlistEdit Pro是一款专为macOS编写的最高级属性列表Plist编辑器。对于Mac和IOS开发人员来说&#xff0c;编写应用程序时必须编辑各种列表文件。PlistEdit Pro通过提供直观且功能强大的界面&#xff0c;使编辑这些文件更加容易。它不仅能够复制和粘贴或拖放属性列表数据&#xf…

三种线程安全的List

在单线程开发环境中&#xff0c;我们经常使用ArrayList作容器来存储我们的数据&#xff0c;但它不是线程安全的&#xff0c;在多线程环境中使用它可能会出现意想不到的结果。 多线程中的ArrayList&#xff1a; 我们可以从一段代码了解并发环境下使用ArrayList的情况&#xff…

用友滴滴:商旅费控领域现实中的1+1>2

当前&#xff0c;AI、5G、大数据等数智技术持续演进&#xff0c;千行百业数智化正在加速。在行业数字化之下&#xff0c;企业服务生态也进入新发展阶段&#xff0c;同时也孕育着新合作、新机遇。 在2023用友生态峰会上&#xff0c;用友与滴滴签署战略合作。用友依托数智化商旅费…

塔望3W消费战略产品案丨筷手小厨:筷手功夫饭的差异化致胜之道

筷手小厨 自热米饭 客户&#xff1a;颐海国际控股有限公司 品牌&#xff1a;筷手小厨 服务&#xff1a;3W消费战略 自热米饭产品案 项目背景 颐海国际控股有限公司,成立于2013年。2016年在香港主板上市&#xff08;股票代码&#xff1a;01579.HK&#xff09;。是一家集研…

使用随机森林回归填补缺失值

文章目录 一、概述二、实现1. 导入需要的库2. 加载数据集3. 构造缺失值4. 使用0和均值填充缺失值5. 使用随机森林填充缺失值6. 对填充好的数据进行建模7. 评估效果对比 一、概述 现实中收集的数据&#xff0c;几乎不可能是完美无缺的&#xff0c;往往都会有一些缺失值。面对缺…

简单沟通,远离纸质繁琐!灵活简单的在线表单制作工具

在线表单制作表单的便利性、灵活性、简易性等优势特点已经成为当今现代化办公中的必需品。随着科技的进步和发展&#xff0c;大到企业、学校&#xff0c;小到集体、个人都需要采用正确的方式去采集信息。鉴于此&#xff0c;在线表单制作工具就是比较理想的记录工具了。那么&…

独立储能的现货电能量与调频辅助服务市场出清协调机制(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …