diff --git a/sql/ruoyi-vue-pro.sql b/sql/ruoyi-vue-pro.sql index 9f3f75a0f..4d3da824d 100644 --- a/sql/ruoyi-vue-pro.sql +++ b/sql/ruoyi-vue-pro.sql @@ -11857,6 +11857,7 @@ INSERT INTO `system_sms_template` VALUES (9, 2, 0, 'bpm_task_assigned', '【工 INSERT INTO `system_sms_template` VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过:{processInstanceName},原因:{comment},查看链接:{detailUrl}', '[\"processInstanceName\",\"comment\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-01-22 00:24:31', b'0'); INSERT INTO `system_sms_template` VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过:{processInstanceName},查看链接:{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', b'0'); INSERT INTO `system_sms_template` VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 6, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2022-04-10 23:22:49', b'0'); +INSERT INTO `system_sms_template` VALUES ('13', '1', '0', 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', '1', 'YUN_PIAN', '1', '2021-10-11 08:10:00', '1', '2021-10-11 08:10:00', '\0'); COMMIT; -- ---------------------------- diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java index f8f267301..cb921ce1e 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java @@ -18,8 +18,8 @@ public enum SmsSceneEnum implements IntArrayValuable { MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆"), MEMBER_UPDATE_MOBILE(2, "user-sms-reset-password", "会员用户 - 修改手机"), - MEMBER_FORGET_PASSWORD(3, "user-sms-update-mobile", "会员用户 - 忘记密码"); - + MEMBER_FORGET_PASSWORD(3, "user-sms-update-mobile", "会员用户 - 忘记密码"), + ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录"); // 如果未来希望管理后台支持手机验证码登陆,可以通过添加 ADMIN_MEMBER_LOGIN 枚举 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SmsSceneEnum::getScene).toArray(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index fe31c20b4..5fc45d01a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -61,6 +61,26 @@ public class AuthController { return success(AuthLoginRespVO.builder().token(token).build()); } + @PostMapping("/sms-login") + @ApiOperation("使用短信验证码登录") + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { + + + + String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); + // 返回结果 + return success(AuthLoginRespVO.builder().token(token).build()); + } + + @PostMapping("/send-login-sms-code") + @ApiOperation(value = "发送手机验证码") + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { + authService.sendSmsCode(getLoginUserId(), reqVO); + return success(true); + } + @GetMapping("/get-permission-info") @ApiOperation("获取登录用户的权限信息") public CommonResult getPermissionInfo() { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java new file mode 100644 index 000000000..4360b9841 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@ApiModel("管理后台 - 短信验证码的呢老姑 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthSmsLoginReqVO { + + @ApiModelProperty(value = "手机号", required = true, example = "yudaoyuanma") + @NotEmpty(message = "手机号不能为空") + @Length(min = 11, max = 11, message = "手机号格式错误,仅支持大陆手机号") + @Pattern(regexp = "^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$", message = "账号格式为数字以及字母") + private String mobile; + + + + @ApiModelProperty(value = "短信验证码", required = true, example = "1024", notes = "验证码开启时,需要传递") + @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) + private String code; + + /** + * 开启验证码的 Group + */ + public interface CodeEnableGroup {} + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java new file mode 100644 index 000000000..a0900e02d --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@ApiModel("管理后台 - 短信验证码的呢老姑 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthSmsSendReqVO { + + @ApiModelProperty(value = "手机号", required = true, example = "yudaoyuanma") + @NotEmpty(message = "手机号不能为空") + @Length(min = 11, max = 11, message = "手机号格式错误,仅支持大陆手机号") + @Pattern(regexp = "^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$", message = "账号格式为数字以及字母") + private String mobile; + + + + @ApiModelProperty(value = "短信验证码", required = true, example = "1024", notes = "验证码开启时,需要传递") + @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) + private String code; + + @ApiModelProperty(value = "短信场景", required = true, example = "1") + @NotEmpty(message = "短信场景", groups = CodeEnableGroup.class) + private Integer scene; + + @ApiModelProperty(value = "验证码的唯一标识", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62", notes = "验证码开启时,需要传递") + @NotEmpty(message = "唯一标识不能为空", groups = AuthLoginReqVO.CodeEnableGroup.class) + private String uuid; + + /** + * 开启验证码的 Group + */ + public interface CodeEnableGroup {} + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index c52328cc0..a9cff93d0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.convert.auth; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; @@ -76,5 +77,5 @@ public interface AuthConvert { SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLogin2ReqVO reqVO); SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO); SocialUserUnbindReqDTO convert(Long userId, Integer userType, AuthSocialUnbindReqVO reqVO); - + SmsCodeSendReqDTO convert(AuthSmsSendReqVO reqVO); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index 53b99ce8c..f7c030be5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -20,6 +20,10 @@ public class SecurityConfiguration { public void customize(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry) { // 登录的接口,可匿名访问 registry.antMatchers(buildAdminApi("/system/login")).anonymous(); + //手机短信验证码登录 + registry.antMatchers(buildAdminApi("/system/sms-login")).anonymous(); + //短信登录验证码接口 + registry.antMatchers(buildAdminApi("/system/send-login-sms-code")).anonymous(); // 验证码的接口 registry.antMatchers(buildAdminApi("/system/captcha/**")).anonymous(); // 获得租户编号的接口 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java index 36ef97972..ffc5315f5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -24,6 +24,23 @@ public interface AdminAuthService extends SecurityAuthFrameworkService { */ String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent); + /** + * 短信验证码发送 + * @param userId + * @param reqVO + */ + public void sendSmsCode(Long userId, AuthSmsSendReqVO reqVO); + + /** + * 短信登录 + * + * @param reqVO 登录信息 + * @param userIp 用户 IP + * @param userAgent 用户 UA + * @return 身份令牌,使用 JWT 方式 + */ + String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) ; + /** * 社交登录,使用 code 授权码 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index efe3d578d..734a501e5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -8,14 +8,14 @@ import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialBindReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialLogin2ReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialLoginReqVO; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.service.common.CaptchaService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.permission.PermissionService; @@ -41,6 +41,7 @@ import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singleton; @@ -74,6 +75,10 @@ public class AdminAuthServiceImpl implements AdminAuthService { @Resource private Validator validator; + @Resource + private SmsCodeApi smsCodeApi; + + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 获取 username 对应的 AdminUserDO @@ -110,6 +115,58 @@ public class AdminAuthServiceImpl implements AdminAuthService { return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); } + @Override + public void sendSmsCode(Long userId, AuthSmsSendReqVO reqVO) { + + this.verifyCaptchaSmsSend(reqVO); + //登录场景,验证是否存在 + if(reqVO.getScene().compareTo(SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene())==0) { + if (userService.getUserByMobile(reqVO.getMobile()) == null) { + throw exception(USER_NOT_EXISTS); + } + } + // TODO 要根据不同的场景,校验是否有用户 + smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); + } + + + /** + * 短信登录 + */ + @Override + public String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) { + + /* 从 Member的AuhtConvert中拷贝出来的,没单独写类 */ + if ( reqVO == null) { + return null; + } + + SmsCodeUseReqDTO smsCodeUseReqDTO = new SmsCodeUseReqDTO(); + smsCodeUseReqDTO.setMobile( reqVO.getMobile() ); + smsCodeUseReqDTO.setCode( reqVO.getCode() ); + smsCodeUseReqDTO.setScene( SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene() ); + smsCodeUseReqDTO.setUsedIp(userIp); + smsCodeApi.useSmsCode(smsCodeUseReqDTO); + + // 获得用户信息 + AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); + + + if(user==null) + { + throw exception(USER_NOT_EXISTS); + } + + cn.hutool.core.lang.Assert.notNull(user, "获取用户失败,结果为空"); + + // 执行登陆 + this.createLoginLog(user.getMobile(),LoginLogTypeEnum.LOGIN_MOBILE, LoginResultEnum.SUCCESS); + LoginUser loginUser = buildLoginUser(user); + + // 缓存登陆用户到 Redis 中,返回 sessionId 编号 + return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); + } + private void verifyCaptcha(AuthLoginReqVO reqVO) { // 如果验证码关闭,则不进行校验 if (!captchaService.isCaptchaEnable()) { @@ -159,6 +216,33 @@ public class AdminAuthServiceImpl implements AdminAuthService { return (LoginUser) authentication.getPrincipal(); } + /** + * 验证验证码并发送短信 + * @param reqVO + */ + private void verifyCaptchaSmsSend(AuthSmsSendReqVO reqVO) { + // 如果验证码关闭,则不进行校验 + if (!captchaService.isCaptchaEnable()) { + return; + } + // 校验验证码 + ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class); + // 验证码不存在 + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; + String code = captchaService.getCaptchaCode(reqVO.getUuid()); + if (code == null) { + throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND); + } + // 验证码不正确 + if (!code.equals(reqVO.getCode())) { + // 创建登录失败日志(验证码不正确) + throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR); + } + // 正确,所以要删除下验证码 + captchaService.deleteCaptchaCode(reqVO.getUuid()); + } + + private void createLoginLog(String username, LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { // 获得用户 AdminUserDO user = userService.getUserByUsername(username); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java index 78d6a88a9..d6a836f01 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java @@ -97,6 +97,15 @@ public interface AdminUserService { */ AdminUserDO getUserByUsername(String username); + /** + * 通过手机号获取用户 + * + * @param mobile 手机号 + * @return 用户对象信息 + */ + AdminUserDO getUserByMobile(String mobile); + + /** * 获得用户分页列表 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index 32f0a8e8c..576cec4a8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -168,6 +168,16 @@ public class AdminUserServiceImpl implements AdminUserService { return userMapper.selectByUsername(username); } + /** + * 通过手机号获取用户 + * @param mobile + * @return + */ + @Override + public AdminUserDO getUserByMobile(String mobile) { + return userMapper.selectByMobile(mobile); + } + @Override public PageResult getUserPage(UserPageReqVO reqVO) { return userMapper.selectPage(reqVO, this.getDeptCondition(reqVO.getDeptId())); diff --git a/yudao-ui-admin/.env.dev b/yudao-ui-admin/.env.dev index ac8b8b096..db9e33854 100644 --- a/yudao-ui-admin/.env.dev +++ b/yudao-ui-admin/.env.dev @@ -5,7 +5,7 @@ ENV = 'development' VUE_APP_TITLE = 芋道管理系统 # 芋道管理系统/开发环境 -VUE_APP_BASE_API = 'http://192.168.225.2' +VUE_APP_BASE_API = 'http://localhost:48080' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/yudao-ui-admin/src/api/login.js b/yudao-ui-admin/src/api/login.js index 740f49795..92cbbeba9 100644 --- a/yudao-ui-admin/src/api/login.js +++ b/yudao-ui-admin/src/api/login.js @@ -100,3 +100,30 @@ export function socialUnbind(type, unionId) { } }) } + +// 获取登录验证码 +export function sendLoginSmsCode(mobile,scene,uuid,code) { + var datas = { + mobile + ,scene + ,uuid, + code + }; + return request({ + url: '/system/send-login-sms-code', + method: 'post', + data: datas + }) +} + +// 短信验证码登录 +export function smsLogin(mobile, code) { + return request({ + url: '/system/sms-login', + method: 'post', + data: { + mobile, + code + } + }) +} \ No newline at end of file diff --git a/yudao-ui-admin/src/assets/images/bg-mobile.png b/yudao-ui-admin/src/assets/images/bg-mobile.png new file mode 100644 index 000000000..92f31781e Binary files /dev/null and b/yudao-ui-admin/src/assets/images/bg-mobile.png differ diff --git a/yudao-ui-admin/src/assets/images/bg.png b/yudao-ui-admin/src/assets/images/bg.png new file mode 100644 index 000000000..385c266e8 Binary files /dev/null and b/yudao-ui-admin/src/assets/images/bg.png differ diff --git a/yudao-ui-admin/src/assets/images/icon.png b/yudao-ui-admin/src/assets/images/icon.png new file mode 100644 index 000000000..204ae4ee9 Binary files /dev/null and b/yudao-ui-admin/src/assets/images/icon.png differ diff --git a/yudao-ui-admin/src/assets/images/pic.png b/yudao-ui-admin/src/assets/images/pic.png new file mode 100644 index 000000000..a74162825 Binary files /dev/null and b/yudao-ui-admin/src/assets/images/pic.png differ diff --git a/yudao-ui-admin/src/assets/styles/login.scss b/yudao-ui-admin/src/assets/styles/login.scss new file mode 100644 index 000000000..74decc9be --- /dev/null +++ b/yudao-ui-admin/src/assets/styles/login.scss @@ -0,0 +1,387 @@ +/* ===== PC DESIGN ===== */ +$W: 1000; +$H: 1920; +$picW: 438; +$picH: 560; +$formW: 320; +$tabW: $formW / 2; +$rowH: 56; +$buttonH: 50; + +// container +$containerBgColor: #e6ebf2; +$containerBgImage: '../assets/images/bg.png'; +// container-logo +$logoWidth: 417px; +$logoHeight: 64px; +$logoImage: '../assets/logo/login-logo.png'; +// container-content +$contentWidth: round($W / $H * 100) * 1vw; +$contentHeight: round($picH / $W * 100) / 100 * $contentWidth; +$contentBgColor: #ffffff; +// container-content-pic +$picWidth: round($picW / $H * 100) * 1vw; +$picHeight: inherit; +$picImage: '../assets/images/pic.png'; +// container-content-field +$fieldWidth: $contentWidth - $picWidth; +$fieldHeight: inherit; +// container-content-field-form +$formWidth: $formW * 1px; +$tabWidth: $tabW * 1px; +$rowHeight: $rowH * 1px; +$buttonHeight: $buttonH * 1px; + +// - - - - - 页面基础设置 +.container { + .login-code { + width: 33%; + height: 38px; + float: right; + img { + cursor: pointer; + width:100%;max-width:100px; height:auto; + vertical-align: middle; + } + } + // 元素 + width: inherit; + height: inherit; + min-width: 1080px; + min-height: 620px; + background-color: $containerBgColor; + background-image: url($containerBgImage); + background-size: cover; + // 定位 + position: relative; + display: flex; + justify-content: center; + align-items: center; + // 文字 + font-size: 14px; + font-family: Microsoft YaHei; + font-weight: 400; + .logo { + // 元素 + width: $logoWidth; + height: $logoHeight; + background-image: url($logoImage); + background-size: contain; + // 定位 + position: absolute; + top: 50px; + left: 50%; + margin-left: -$logoWidth/2; + } + .content { + // 元素 + width: $contentWidth; + height: $contentHeight; + background-color: #ffffff; + box-shadow: 0px 16px 40px rgba(0, 0, 0, 0.07); + border-radius: 20px; + // 定位 + position: relative; + .pic { + // 元素 + width: $picWidth; + height: $picHeight; + background-image: url($picImage); + background-repeat: no-repeat; + background-size: cover; + border-radius: 20px 0 0 20px; + // 定位 + position: absolute; + top: 0; + left: 0; + } + .field { + width: $fieldWidth; + height: $fieldHeight; + // 定位 + position: absolute; + top: 0; + left: $picWidth; + display:flex; + justify-content: center; + align-items: center; + .pc-title{ width: 100%; clear: both;} + .mobile-title, + .mobile-switch { + display: none; + } + .form { + box-sizing: border-box; + width: $formWidth; + // - - - tab + :deep(.el-tabs__content) { + padding: 20px 0 0; + } + :deep(.el-tabs__item) { + // 元素 + width: $tabWidth; + height: $rowHeight; + padding: 0; + // 文字 + line-height: $rowHeight; + color: #666666; + } + :deep(.el-tabs__item.is-active) { + font-weight: bold; + color: #2F53EB; + } + :deep(.el-tabs__active-bar) { + height: 3px; + border-radius: 2px; + } + // - - - input + :deep(.el-input__inner) { + // 元素 + width: 100%; + height: $rowHeight; + background: #f5f5f5; + border: 0; + border-radius: 28px; + // 文字 + text-align: center; + line-height: 19px; + color: #262626; + } + .code:deep(.el-input__inner) { + padding: 0 24px; + // 文字 + text-align: left; + } + :deep(.el-input__inner::-webkit-input-placeholder) { /* WebKit browsers */ + font-weight: 400; + color: #8C8C8C; + } + :deep(.el-input__inner:-moz-placeholder) { /* Mozilla Firefox 4 to 18 */ + font-weight: 400; + color: #8C8C8C; + } + :deep(.el-input__inner::-moz-placeholder) { /* Mozilla Firefox 19+ */ + font-weight: 400; + color: #8C8C8C; + opacity:1; + } + :deep(.el-input__inner:-ms-input-placeholder) { /* Internet Explorer 10+ */ + font-weight: 400; + color: #8C8C8C !important; + } + :deep(.el-form-item) { + position: relative; + .button-code { + // 元素 + height: $rowHeight; + box-sizing: border-box; + // 定位 + position: absolute; + top: 0; + right: 20px; + z-index: 1; + // 文字 + line-height: 20px; + font-size: 14px; + font-family: PingFang SC; + font-weight: 400; + color: #2F53EB; + span { + padding-left: 15px; + border-left: 2px solid #D9D9D9; + } + } + } + :deep(.el-form-item__error) { + padding-left: 24px; + } + .button { + width: 100%; + height: $buttonHeight; + background: rgba(24, 144, 255, 0.2); + border: 0; + border-radius: 24px; + margin-bottom: 20px; + // 文字 + line-height: 26px; + font-size: 20px; + color: #FFFFFF; + } + .button-active { + background: #2F53EB; + box-shadow: 0px 2px 8px rgba(0, 80, 184, 0.2); + } + } + } + } + .footer { + // 元素 + height: 16px; + line-height: 16px; + font-size: 12px; + color: #8c8c8c; + // 定位 + position: absolute; + bottom: 30px; + a, + a:hover, + a:active { + color: inherit; + text-decoration: none; + } + } +} + +// - - - - - PC 最小尺寸设置 +@media screen and (min-width: 599px) and (max-width: 1366px) { + .container { + .content { + width: 710px; + height: 397px; + .pic { + width: 314px; + } + .field { + width: calc(710px - 314px); + left: 314px; + .form { + width: 320px; + :deep(.el-input__inner) { + width: 320px; + height: 56px; + } + .button { + height: 50px; + } + } + } + } + } +} + + +/* ===== MOBILE DESIGN ===== */ +$mobileW: 375; +$mobileH: 812; +$mobileContentW: 327; +$mobileContentH: 376; +$mobileFormW: 280; +$mobileRowH: 48; +$mobileButtonH: 48; + +// container +$mobileContainerBgImage: '../assets/images/bg-mobile.png'; +// container-content +$mobileContentWidth: round($mobileContentW / $mobileW * 100) * 1vw; +$mobileContentHeight: round($mobileContentH / $mobileW * 100) / 100 * $mobileContentWidth; +// container-content-field-form +$mobileFormWidth: round($mobileFormW / $mobileW *100) * 1vw; +$mobileRowHeight: $mobileRowH * 1px; +$mobileButtonHeight: $mobileButtonH * 1px; +$iconBgImage: '../assets/images/icon.png'; + +// - - - - - 移动端设置 +@media screen and (max-width: 599px) { + .container { + // 元素 + background-image: url($mobileContainerBgImage); + min-width: 280px; + min-height: 568px; + // 文字 + font-size: 17px; + font-family: PingFang SC; + font-weight: bold; + .logo { + display: none; + } + + .content { + // 元素 + width: $mobileContentWidth; + height: $mobileContentHeight; + min-width: 250px; + min-height: 340px; + // 定位 + display: flex; + justify-content: center; + align-items: center; + .pic { + display: none; + } + .field { + // 元素 + width: inherit; + min-height: inherit; + // 定位 + left: 0; + display: flex; + flex-direction: column; + .mobile-title { + // 元素 + margin: 0 0 20px; + display: block; + } + .form { + width: $mobileFormWidth; + // - - - tab + :deep(.el-tabs__header) { + display: none; + } + :deep(.el-tabs__content) { + padding: 0; + } + // - - - input + :deep(.el-input__inner) { + height: $mobileRowHeight; + line-height: 24px; + // 文字 + text-align: center; + color: #262626; + } + :deep(.el-form-item) { + .button-code { + // 元素 + height: $mobileRowHeight; + } + } + .button { + height: $mobileButtonHeight; + line-height: 24px; + color: #FFFFFF; + } + } + .mobile-switch { + display: block; + line-height: 20px; + font-size: 14px; + font-weight: 400; + color: #595959; + margin: 0; + .icon { + width: 14px; + height: 14px; + display: inline-block; + background-image: url($iconBgImage); + background-size: cover; + } + } + .mobile-switch:hover { + cursor: pointer; + } + } + } + .footer { + // 元素 + font-size: 12px; + font-family: PingFang SC; + font-weight: 400; + line-height: 17px; + color: #333333; + opacity: 0.6; + // 定位 + position: absolute; + bottom: 20px; + } + } + +} diff --git a/yudao-ui-admin/src/store/modules/user.js b/yudao-ui-admin/src/store/modules/user.js index 78da4ece4..969f825ac 100644 --- a/yudao-ui-admin/src/store/modules/user.js +++ b/yudao-ui-admin/src/store/modules/user.js @@ -1,4 +1,4 @@ -import {login, logout, getInfo, socialLogin, socialLogin2} from '@/api/login' +import {login, logout, getInfo, socialLogin, socialLogin2,smsLogin} from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' const user = { @@ -86,7 +86,21 @@ const user = { }) }) }, - + // 登录 + SmsLogin({ commit }, userInfo) { + const mobile = userInfo.mobile.trim() + const mobileCode = userInfo.mobileCode + return new Promise((resolve, reject) => { + smsLogin(mobile,mobileCode).then(res => { + res = res.data; + setToken(res.token) + commit('SET_TOKEN', res.token) + resolve() + }).catch(error => { + reject(error) + }) + }) +}, // 获取用户信息 GetInfo({ commit, state }) { return new Promise((resolve, reject) => { diff --git a/yudao-ui-admin/src/views/login.vue b/yudao-ui-admin/src/views/login.vue index 95ddb9862..8f62d9eee 100644 --- a/yudao-ui-admin/src/views/login.vue +++ b/yudao-ui-admin/src/views/login.vue @@ -1,56 +1,117 @@ -