Merge branch 'dev' of https://git.yfgame.vip/r/hangtag into dev
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
|
|
@ -24,11 +24,20 @@ public class ValidationUtils {
|
|||
|
||||
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
|
||||
|
||||
private static final Pattern PATTERN_EMAIL = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
|
||||
|
||||
public static boolean isMobile(String mobile) {
|
||||
return StringUtils.hasText(mobile)
|
||||
&& PATTERN_MOBILE.matcher(mobile).matches();
|
||||
}
|
||||
|
||||
public static boolean isEmail(String email) {
|
||||
|
||||
return StringUtils.hasText(email)
|
||||
&& PATTERN_EMAIL.matcher(email).matches();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isURL(String url) {
|
||||
return StringUtils.hasText(url)
|
||||
&& PATTERN_URL.matcher(url).matches();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.hangtag.framework.common.validation;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(
|
||||
validatedBy = EmailValidator.class
|
||||
)
|
||||
public @interface Email {
|
||||
|
||||
String message() default "邮箱格式不正确";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package cn.hangtag.framework.common.validation;
|
||||
|
||||
import cn.hangtag.framework.common.util.validation.ValidationUtils;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class EmailValidator implements ConstraintValidator<Email, String> {
|
||||
|
||||
@Override
|
||||
public void initialize(Email annotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
// 如果手机号为空,默认不校验,即校验通过
|
||||
if (StrUtil.isEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
// 校验手机
|
||||
return ValidationUtils.isEmail(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,5 +24,6 @@ public interface ErrorCodeConstants extends cn.hangtag.module.system.enums.Erro
|
|||
ErrorCode PRODUCE_ORDER_IMPORT_LIST_IS_EMPTY = new ErrorCode(4003, "导入生产制单数据不能为空");
|
||||
ErrorCode SALE_CONTRACT_NOT_EXISTS = new ErrorCode(5000, "OMS销售合约不存在");
|
||||
ErrorCode PRODUCT_PRICE_NOT_EXISTS = new ErrorCode(600, "产品单价记录不存在");
|
||||
ErrorCode CUSTOMER_EMAIL_EXISTS = new ErrorCode(600, "已存在重复的客户邮箱号");
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import cn.hangtag.module.oms.controller.admin.customer.front.vo.AddressInfoVO;
|
|||
import cn.hangtag.module.oms.controller.admin.customer.front.vo.CustomerInfoVO;
|
||||
import cn.hangtag.module.oms.serialnumber.CodingRulesUtils;
|
||||
import cn.hangtag.module.system.controller.admin.user.vo.user.UserSaveReqVO;
|
||||
import cn.hangtag.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.hangtag.module.system.service.permission.PermissionService;
|
||||
import cn.hangtag.module.system.service.user.AdminUserService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
|
@ -74,9 +75,15 @@ public class CustomerServiceImpl implements CustomerService {
|
|||
customer.setNumber(getNewCode());
|
||||
}
|
||||
|
||||
// 判断是否存在重复的邮箱账户
|
||||
AdminUserDO user = userService.getUserByMail(createReqVO.getEmail(), 999999);
|
||||
if(user!=null){
|
||||
throw exception(CUSTOMER_EMAIL_EXISTS);
|
||||
}
|
||||
|
||||
//新增用户账号
|
||||
UserSaveReqVO userSaveReqVO = new UserSaveReqVO();
|
||||
userSaveReqVO.setUsername(createReqVO.getPhone());
|
||||
userSaveReqVO.setUsername(createReqVO.getEmail());
|
||||
userSaveReqVO.setNickname(createReqVO.getName());
|
||||
userSaveReqVO.setMobile(createReqVO.getPhone());
|
||||
userSaveReqVO.setPassword(userInitPassword);
|
||||
|
|
@ -94,8 +101,6 @@ public class CustomerServiceImpl implements CustomerService {
|
|||
// 插入子表
|
||||
createCustomerAddressList(customer.getId(), createReqVO.getCustomerAddresss());
|
||||
|
||||
|
||||
|
||||
// 返回
|
||||
return customer.getId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package cn.hangtag.module.system.api.mail;
|
||||
|
||||
import cn.hangtag.framework.common.exception.ServiceException;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 短信验证码 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MailCodeApi {
|
||||
|
||||
/**
|
||||
* 创建邮箱验证码,并进行发送
|
||||
*
|
||||
* @param reqDTO 发送请求
|
||||
*/
|
||||
void sendMailCode(@Valid MailCodeSendReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 验证邮箱验证码,并进行使用
|
||||
* 如果正确,则将验证码标记成已使用
|
||||
* 如果错误,则抛出 {@link ServiceException} 异常
|
||||
*
|
||||
* @param reqDTO 使用请求
|
||||
*/
|
||||
void useMailCode(@Valid MailCodeUseReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
* @param reqDTO 校验请求
|
||||
*/
|
||||
void validateMailCode(@Valid MailCodeValidateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import javax.validation.constraints.NotNull;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 邮件发送 Request DTO
|
||||
* 邮箱发送 Request DTO
|
||||
*
|
||||
* @author wangjingqi
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.hangtag.module.system.api.mail.dto.code;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 邮箱验证码的发送 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class MailCodeSendReqDTO {
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
private String mail;
|
||||
/**
|
||||
* 发送场景
|
||||
*/
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(MailSceneEnum.class)
|
||||
private Integer scene;
|
||||
/**
|
||||
* 发送 IP
|
||||
*/
|
||||
@NotEmpty(message = "发送 IP 不能为空")
|
||||
private String createIp;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package cn.hangtag.module.system.api.mail.dto.code;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 邮箱验证码的使用 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class MailCodeUseReqDTO {
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
private String mail;
|
||||
/**
|
||||
* 发送场景
|
||||
*/
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(SmsSceneEnum.class)
|
||||
private Integer scene;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@NotEmpty(message = "验证码")
|
||||
private String code;
|
||||
/**
|
||||
* 使用 IP
|
||||
*/
|
||||
@NotEmpty(message = "使用 IP 不能为空")
|
||||
private String usedIp;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.hangtag.module.system.api.mail.dto.code;
|
||||
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 邮箱验证码的校验 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class MailCodeValidateReqDTO {
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Mobile
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
private String mail;
|
||||
/**
|
||||
* 发送场景
|
||||
*/
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(SmsSceneEnum.class)
|
||||
private Integer scene;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@NotEmpty(message = "验证码")
|
||||
private String code;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package cn.hangtag.module.system.enums.mail;
|
||||
|
||||
import cn.hangtag.framework.common.core.IntArrayValuable;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 用户邮箱验证码发送场景的枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MailSceneEnum implements IntArrayValuable {
|
||||
|
||||
MEMBER_LOGIN(1, "user-mail-login", "会员用户 - 邮箱登陆"),
|
||||
MEMBER_UPDATE_MOBILE(2, "user-update-mail", "会员用户 - 修改手机"),
|
||||
MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"),
|
||||
MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"),
|
||||
|
||||
ADMIN_MEMBER_LOGIN(21, "admin-mail-login", "后台用户 - 邮箱登录");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MailSceneEnum::getScene).toArray();
|
||||
|
||||
/**
|
||||
* 验证场景的编号
|
||||
*/
|
||||
private final Integer scene;
|
||||
/**
|
||||
* 模版编码
|
||||
*/
|
||||
private final String templateCode;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static MailSceneEnum getCodeByScene(Integer scene) {
|
||||
return ArrayUtil.firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene),
|
||||
values());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package cn.hangtag.module.system.api.mail;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.SmsCodeApi;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.service.mail.MailCodeService;
|
||||
import cn.hangtag.module.system.service.sms.SmsCodeService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 短信验证码 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class MailCodeApiImpl implements MailCodeApi {
|
||||
|
||||
@Resource
|
||||
private MailCodeService mailCodeService;
|
||||
|
||||
@Override
|
||||
public void sendMailCode(MailCodeSendReqDTO reqDTO) {
|
||||
mailCodeService.sendMailCode(reqDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useMailCode(MailCodeUseReqDTO reqDTO) {
|
||||
mailCodeService.useMailCode(reqDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateMailCode(MailCodeValidateReqDTO reqDTO) {
|
||||
mailCodeService.validateMailCode(reqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -146,6 +146,27 @@ public class AuthController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
// ========== 邮箱相关 ==========
|
||||
|
||||
@PostMapping("/send-mail-code")
|
||||
@PermitAll
|
||||
@Operation(summary = "发送邮箱验证码")
|
||||
public CommonResult<Boolean> sendForgetPasswordEmailCode(@RequestBody @Valid AuthMailSendReqVO reqVO) {
|
||||
authService.sendMailCode(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/mail-modifypwd")
|
||||
@PermitAll
|
||||
@Operation(summary = "使用邮箱验证码修改密码")
|
||||
public CommonResult<Boolean> smsLogin(@RequestBody @Valid AuthMailModifyPwdReqVO reqVO) {
|
||||
authService.mailResetPwd(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========== 社交登录相关 ==========
|
||||
|
||||
@GetMapping("/social-auth-redirect")
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public class AuthLoginReqVO {
|
|||
|
||||
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "hangtagyuanma")
|
||||
@NotEmpty(message = "登录账号不能为空")
|
||||
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
//@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
//@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package cn.hangtag.module.system.controller.admin.auth.vo;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Schema(description = "管理后台 - 邮箱验证码的重置密码 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthMailModifyPwdReqVO {
|
||||
|
||||
@Schema(description = "邮箱账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "邮箱账号不能为空")
|
||||
@Email
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "确认密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "确认密码不能为空")
|
||||
private String checkpassword;
|
||||
|
||||
@Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotEmpty(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "账户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotEmpty(message = "账户类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package cn.hangtag.module.system.controller.admin.auth.vo;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 发送手机验证码 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthMailSendReqVO {
|
||||
|
||||
@Schema(description = "邮箱账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "邮箱账号不能为空")
|
||||
@Email
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "邮箱场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(MailSceneEnum.class)
|
||||
private Integer scene;
|
||||
|
||||
@Schema(description = "账户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "账户类型不能为空")
|
||||
private Integer type;
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package cn.hangtag.module.system.convert.auth;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hangtag.framework.common.util.object.BeanUtils;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
|
|
@ -85,4 +87,8 @@ public interface AuthConvert {
|
|||
|
||||
SmsCodeUseReqDTO convert(AuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
MailCodeSendReqDTO convert(AuthMailSendReqVO reqVO);
|
||||
|
||||
MailCodeUseReqDTO convert(AuthMailModifyPwdReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package cn.hangtag.module.system.dal.dataobject.mail;
|
||||
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 邮箱验证码 DO
|
||||
*
|
||||
* idx_mobile 索引:基于 {@link #mobile} 字段
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("system_mail_code")
|
||||
@KeySequence("system_mail_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MailCodeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 邮箱号
|
||||
*/
|
||||
private String mail;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 发送场景
|
||||
*
|
||||
* 枚举 {@link MailCodeDO}
|
||||
*/
|
||||
private Integer scene;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
private String createIp;
|
||||
/**
|
||||
* 今日发送的第几条
|
||||
*/
|
||||
private Integer todayIndex;
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean used;
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
private LocalDateTime usedTime;
|
||||
/**
|
||||
* 使用 IP
|
||||
*/
|
||||
private String usedIp;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package cn.hangtag.module.system.dal.mysql.mail;
|
||||
|
||||
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.hangtag.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.hangtag.module.system.dal.dataobject.mail.MailCodeDO;
|
||||
import cn.hangtag.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MailCodeMapper extends BaseMapperX<MailCodeDO> {
|
||||
|
||||
/**
|
||||
* 获得手机号的最后一个手机验证码
|
||||
*
|
||||
* @param mail 手机号
|
||||
* @param scene 发送场景,选填
|
||||
* @param code 验证码 选填
|
||||
* @return 手机验证码
|
||||
*/
|
||||
default MailCodeDO selectLastByMail(String mail, String code, Integer scene) {
|
||||
return selectOne(new QueryWrapperX<MailCodeDO>()
|
||||
.eq("mail", mail)
|
||||
.eqIfPresent("scene", scene)
|
||||
.eqIfPresent("code", code)
|
||||
.orderByDesc("id")
|
||||
.limitN(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,6 +25,10 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
|
|||
return selectOne(AdminUserDO::getEmail, email);
|
||||
}
|
||||
|
||||
default AdminUserDO selectByEmail(String email,Integer type) {
|
||||
return selectOne(AdminUserDO::getEmail, email,AdminUserDO::getDeptId,type);
|
||||
}
|
||||
|
||||
default AdminUserDO selectByMobile(String mobile) {
|
||||
return selectOne(AdminUserDO::getMobile, mobile);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,4 +70,14 @@ public interface AdminAuthService {
|
|||
*/
|
||||
AuthLoginRespVO refreshToken(String refreshToken);
|
||||
|
||||
|
||||
/**
|
||||
* 邮箱验证码发送
|
||||
*
|
||||
* @param reqVO 发送请求
|
||||
*/
|
||||
void sendMailCode(AuthMailSendReqVO reqVO);
|
||||
|
||||
|
||||
boolean mailResetPwd(AuthMailModifyPwdReqVO reqVO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package cn.hangtag.module.system.service.auth;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.MailCodeApi;
|
||||
import cn.hangtag.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hangtag.framework.common.enums.CommonStatusEnum;
|
||||
import cn.hangtag.framework.common.enums.UserTypeEnum;
|
||||
|
|
@ -29,6 +32,7 @@ import com.xingyuv.captcha.model.vo.CaptchaVO;
|
|||
import com.xingyuv.captcha.service.CaptchaService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
|
@ -64,6 +68,13 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
private CaptchaService captchaService;
|
||||
@Resource
|
||||
private SmsCodeApi smsCodeApi;
|
||||
@Resource
|
||||
private MailCodeApi mailCodeApi;
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@Resource
|
||||
private AdminUserMapper userMapper;
|
||||
|
||||
|
||||
/**
|
||||
* 验证码的开关,默认为 true
|
||||
|
|
@ -247,4 +258,43 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
return UserTypeEnum.ADMIN;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendMailCode(AuthMailSendReqVO reqVO) {
|
||||
// 登录场景,验证是否存在
|
||||
if (userService.getUserByMail(reqVO.getMail(),reqVO.getType()) == null) {
|
||||
throw exception(MAIL_ACCOUNT_NOT_EXISTS);
|
||||
}
|
||||
// 发送验证码
|
||||
mailCodeApi.sendMailCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mailResetPwd(AuthMailModifyPwdReqVO reqVO) {
|
||||
// 校验验证码
|
||||
mailCodeApi.useMailCode(AuthConvert.INSTANCE.convert(reqVO, MailSceneEnum.MEMBER_RESET_PASSWORD.getScene(), getClientIP()));
|
||||
|
||||
// 获得用户信息
|
||||
AdminUserDO user = userService.getUserByMail(reqVO.getMail(),reqVO.getType());
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
// 执行更新
|
||||
user.setPassword(encodePassword(reqVO.getPassword())); // 加密密码
|
||||
int i = userMapper.updateById(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
private String encodePassword(String password) {
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package cn.hangtag.module.system.service.mail;
|
||||
|
||||
import cn.hangtag.framework.common.exception.ServiceException;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 短信验证码 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MailCodeService {
|
||||
|
||||
/**
|
||||
* 创建邮件验证码,并进行发送
|
||||
*
|
||||
* @param reqDTO 发送请求
|
||||
*/
|
||||
void sendMailCode(@Valid MailCodeSendReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 验证邮件验证码,并进行使用
|
||||
* 如果正确,则将验证码标记成已使用
|
||||
* 如果错误,则抛出 {@link ServiceException} 异常
|
||||
*
|
||||
* @param reqDTO 使用请求
|
||||
*/
|
||||
void useMailCode(@Valid MailCodeUseReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
* @param reqDTO 校验请求
|
||||
*/
|
||||
void validateMailCode(@Valid MailCodeValidateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package cn.hangtag.module.system.service.mail;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.dal.dataobject.mail.MailCodeDO;
|
||||
import cn.hangtag.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import cn.hangtag.module.system.dal.mysql.mail.MailCodeMapper;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
|
||||
import cn.hangtag.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.isToday;
|
||||
import static cn.hangtag.module.system.enums.ErrorCodeConstants.*;
|
||||
import static cn.hutool.core.util.RandomUtil.randomInt;
|
||||
|
||||
/**
|
||||
* 邮箱验证码 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class MailCodeServiceImpl implements MailCodeService {
|
||||
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
@Resource
|
||||
private MailCodeMapper mailCodeMapper;
|
||||
|
||||
@Resource
|
||||
private MailSendService mailSendService;
|
||||
|
||||
@Override
|
||||
public void sendMailCode(MailCodeSendReqDTO reqDTO) {
|
||||
MailSceneEnum sceneEnum = MailSceneEnum.getCodeByScene(reqDTO.getScene());
|
||||
Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene());
|
||||
// 创建验证码
|
||||
String code = createMailCode(reqDTO.getMail(), reqDTO.getScene(), reqDTO.getCreateIp());
|
||||
|
||||
LinkedHashMap<String, Object> params = new LinkedHashMap<>();
|
||||
params.put("key1",reqDTO.getMail());
|
||||
params.put("key2",reqDTO.getMail());
|
||||
params.put("key3",code);
|
||||
String templateCode = sceneEnum.getTemplateCode();
|
||||
|
||||
// 发送验证码
|
||||
mailSendService.sendSingleMail(reqDTO.getMail(), null, null,
|
||||
sceneEnum.getTemplateCode(), params);
|
||||
}
|
||||
|
||||
private String createMailCode(String mobile, Integer scene, String ip) {
|
||||
// 校验是否可以发送验证码,不用筛选场景
|
||||
MailCodeDO lastMailCode = mailCodeMapper.selectLastByMail(mobile, null, null);
|
||||
if (lastMailCode != null) {
|
||||
if (LocalDateTimeUtil.between(lastMailCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
|
||||
throw exception(SMS_CODE_SEND_TOO_FAST);
|
||||
}
|
||||
if (isToday(lastMailCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限
|
||||
lastMailCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
|
||||
throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
|
||||
}
|
||||
// TODO 芋艿:提升,每个 IP 每天可发送数量
|
||||
// TODO 芋艿:提升,每个 IP 每小时可发送数量
|
||||
}
|
||||
|
||||
// 创建验证码记录
|
||||
String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d",
|
||||
randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
|
||||
MailCodeDO newSmsCode = MailCodeDO.builder().mail(mobile).code(code).scene(scene)
|
||||
.todayIndex(lastMailCode != null && isToday(lastMailCode.getCreateTime()) ? lastMailCode.getTodayIndex() + 1 : 1)
|
||||
.createIp(ip).used(false).build();
|
||||
mailCodeMapper.insert(newSmsCode);
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useMailCode(MailCodeUseReqDTO reqDTO) {
|
||||
// 检测验证码是否有效
|
||||
MailCodeDO lastMailCode = validateMailCode0(reqDTO.getMail(), reqDTO.getCode(), reqDTO.getScene());
|
||||
// 使用验证码
|
||||
mailCodeMapper.updateById(MailCodeDO.builder().id(lastMailCode.getId())
|
||||
.used(true).usedTime(LocalDateTime.now()).usedIp(reqDTO.getUsedIp()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateMailCode(MailCodeValidateReqDTO reqDTO) {
|
||||
validateMailCode0(reqDTO.getMail(), reqDTO.getCode(), reqDTO.getScene());
|
||||
}
|
||||
|
||||
private MailCodeDO validateMailCode0(String mobile, String code, Integer scene) {
|
||||
// 校验验证码
|
||||
MailCodeDO lastMailCode = mailCodeMapper.selectLastByMail(mobile, code, scene);
|
||||
// 若验证码不存在,抛出异常
|
||||
if (lastMailCode == null) {
|
||||
throw exception(SMS_CODE_NOT_FOUND);
|
||||
}
|
||||
// 超过时间
|
||||
if (LocalDateTimeUtil.between(lastMailCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
>= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
|
||||
throw exception(SMS_CODE_EXPIRED);
|
||||
}
|
||||
// 判断验证码是否已被使用
|
||||
if (Boolean.TRUE.equals(lastMailCode.getUsed())) {
|
||||
throw exception(SMS_CODE_USED);
|
||||
}
|
||||
return lastMailCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import cn.hangtag.module.system.dal.mysql.mail.MailTemplateMapper;
|
|||
import cn.hangtag.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -41,7 +42,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
|
|||
/**
|
||||
* 正则表达式,匹配 {} 中的变量
|
||||
*/
|
||||
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
|
||||
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{\\{(.*?)}}");
|
||||
|
||||
@Resource
|
||||
private MailTemplateMapper mailTemplateMapper;
|
||||
|
|
@ -122,6 +123,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
|
|||
|
||||
@Override
|
||||
public String formatMailTemplateContent(String content, Map<String, Object> params) {
|
||||
if(StringUtils.isNotBlank(content)){
|
||||
/* content = content.replaceFirst("<p>", "").replace("</p>", "");
|
||||
content = content.replaceAll("<", "<").replaceAll(">", ">");
|
||||
content = content.replaceAll(" ", "");*/
|
||||
content = content.replaceAll("\\{\\{", "{").replaceAll("}}","}");
|
||||
}
|
||||
return StrUtil.format(content, params);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -201,4 +201,15 @@ public interface AdminUserService {
|
|||
*/
|
||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过邮箱号获取用户
|
||||
*
|
||||
* @param mail 邮箱号
|
||||
* @return 用户对象信息
|
||||
*/
|
||||
AdminUserDO getUserByMail(String mail,Integer type);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -496,6 +496,11 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminUserDO getUserByMail(String mail,Integer type) {
|
||||
return userMapper.selectByEmail(mail,type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<el-form label-width="180px">
|
||||
<el-form label-width="200px">
|
||||
<el-form-item label="Style" v-show="that.draftDesignList.length > 1">
|
||||
<div class="flex ml-3">
|
||||
<div>
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
</div>
|
||||
<div class="w-full" v-if="that.propOrderByList && that.propOrderByList.length > 0">
|
||||
<el-scrollbar height="600px">
|
||||
<el-form label-width="180px">
|
||||
<el-form label-width="200px">
|
||||
<el-form-item
|
||||
v-for="(tmp,orderIndex) in that.propOrderByList"
|
||||
|
||||
|
|
@ -134,8 +134,9 @@
|
|||
:class="{
|
||||
'error_tip': errorItem(tmp.key,i)
|
||||
}" v-if="that.propInfo[tmp.key].canInput">
|
||||
|
||||
<el-autocomplete
|
||||
style="min-width: 200px;width: 60%"
|
||||
style="min-width: 320px;width: 100%"
|
||||
v-model="that.propInfo[tmp.key].dataInfo[i].showLabel"
|
||||
:fetch-suggestions="querySearch"
|
||||
clearable
|
||||
|
|
@ -500,6 +501,7 @@ const changeIconData = (index: number, key: string) => {
|
|||
for (let i = 0; i < infoList.length; i++) {
|
||||
mapping[infoList[i].label] = infoList[i].langMapping;
|
||||
}
|
||||
that.propInfo[key].processType = findProcessTypeIndex(index);
|
||||
// 多语言变量替换
|
||||
const allData = [...that.propInfo[key].dataInfo]
|
||||
const fLang = allData[0].locale;
|
||||
|
|
@ -563,7 +565,7 @@ const findProcessTypeIndex = (index) => {
|
|||
const washingInfoListByType = (type, index = -1) => {
|
||||
const typeIndex = findProcessTypeIndex(index);
|
||||
return that.washingInfoList.filter(item => {
|
||||
if (item.type === type && (typeIndex < 0 || (item.processType === `${typeIndex}`))) {
|
||||
if (`${item.type}` === `${type}` && (typeIndex < 0 || (`${item.processType}` === `${typeIndex}`))) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
|
@ -896,18 +898,17 @@ const checkPropInfo = (info) => {
|
|||
for (let i = 0; i < that.propOrderByList.length; i++) {
|
||||
keys.push(that.propOrderByList[i].key);
|
||||
}
|
||||
|
||||
endloop: for (let i = 0; i < keys.length; i++) {
|
||||
console.log("keys",keys)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
// 合并相同语言的文字
|
||||
let newArr = [];
|
||||
// 每种语言合并成一句话
|
||||
const allLang = findGroupAllLang(key, newInfo2);
|
||||
const firstLang = newInfo[key].dataInfo[0].locale;
|
||||
const linkChar = newInfo[key].linkChar || '';
|
||||
const dataInfoArr = newInfo[key].dataInfo;
|
||||
let langList = newInfo[key].langList || [];
|
||||
console.log("newInfo[key].groupType",newInfo[key])
|
||||
if (newInfo[key].groupType === GroupTypeEnum.RATIO) {
|
||||
|
||||
// 成分比例 都进行固定语言组合
|
||||
// 获取到所以的成分
|
||||
let langSort = newInfo[key].langSort || [];
|
||||
|
|
@ -937,6 +938,8 @@ const checkPropInfo = (info) => {
|
|||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
let allRatio = 0; // 累计 比例
|
||||
const langLabel = [];
|
||||
let linkChar = ","
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
if (langSort[j] === langList[k].locale) {
|
||||
let tmpValue = `${langList[k].value}`.replaceAll('${r}', `${str}`)
|
||||
|
|
@ -946,9 +949,17 @@ const checkPropInfo = (info) => {
|
|||
if (str === null || str === undefined || str === -1) {
|
||||
str = ''
|
||||
}
|
||||
mergeLabel.push(tmpValue)
|
||||
console.log("langList[k]",langList[k])
|
||||
if(langList[k].linkChar && linkChar != ','){
|
||||
linkChar = langList[k]
|
||||
}
|
||||
langLabel.push(tmpValue)
|
||||
}
|
||||
}
|
||||
if(langLabel.length > 0){
|
||||
mergeLabel.push(langLabel.join(linkChar))
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
// 校验
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
|
|
@ -1006,7 +1017,6 @@ const checkPropInfo = (info) => {
|
|||
if (newInfo[kKey].groupName === newInfo[key].parentId) {
|
||||
newKey = kKey;
|
||||
parentStr = newInfo[kKey].dataInfo[0].label + '<br/>'
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1021,24 +1031,28 @@ const checkPropInfo = (info) => {
|
|||
|
||||
// 语言对应自己的自动位置
|
||||
if (newInfo[key].multiLanguage) {
|
||||
const showLabel1 = newInfo[key].dataInfo[0].showLabel
|
||||
if (newInfo[key].groupType === GroupTypeEnum.ICON) {
|
||||
const infoList = washingInfoListByType(newInfo[key].groupType)
|
||||
const infos = newInfo[key].dataInfo;
|
||||
for (let j = 1; j < infos.length; j++) {
|
||||
|
||||
outerLoop: for (let i = 0; i < infoList.length; i++) {
|
||||
const row1 = infoList[i];
|
||||
for (let k = 0; k < row1.langMapping.length; k++) {
|
||||
// 跳过icon 类型,因为没有url
|
||||
if (infos[j].locale === row1.langMapping[k].locale) {
|
||||
infos[j].label = row1.langMapping[k].value
|
||||
break outerLoop;
|
||||
if(`${row1.processType}` === `${ newInfo[key].processType}`){
|
||||
for (let k = 0; k < row1.langMapping.length; k++) {
|
||||
// 跳过icon 类型,因为没有url
|
||||
if (infos[j].locale === row1.langMapping[k].locale && showLabel1 === row1.value) {
|
||||
infos[j].label = row1.langMapping[k].value
|
||||
break outerLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("newArr33",infos)
|
||||
newInfo[key].dataInfo = infos;
|
||||
break;
|
||||
|
||||
}else {
|
||||
const infoList = washingInfoListByType(newInfo[key].groupType)
|
||||
const infos = newInfo[key].dataInfo;
|
||||
|
|
@ -1054,41 +1068,55 @@ const checkPropInfo = (info) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
console.log("newArr33",infos)
|
||||
console.log("newArr99",infos)
|
||||
newInfo[key].dataInfo = infos;
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
// 多种语言组合成一句话
|
||||
// 文本类型处理
|
||||
if (newInfo[key].groupType === GroupTypeEnum.TEXT) {
|
||||
|
||||
let langSort = newInfo[key].langSort || [];
|
||||
if (langSort.length === 0) {
|
||||
langSort = allLang
|
||||
}
|
||||
let mergeLabel = [];
|
||||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
if (langSort[j] === langList[k].locale && newInfo[key].dataInfo[k].showLabel) {
|
||||
mergeLabel.push(langList[k].value)
|
||||
|
||||
if(newInfo[key].canInput){
|
||||
for (let j = 0; j < dataInfoArr.length; j++) {
|
||||
if (dataInfoArr[j].showLabel) {
|
||||
if(mergeLabel.includes(dataInfoArr[j].showLabel)){
|
||||
useMessage().notifyWarning(`${newInfo[key].groupName}中,第${j + 1}项重复${dataInfoArr[j].showLabel}`)
|
||||
}
|
||||
mergeLabel.push(dataInfoArr[j].showLabel)
|
||||
}
|
||||
}
|
||||
// 校验重复
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
if (langSort[j] === newInfo[key].dataInfo[k].locale) {
|
||||
if (newInfo[key].dataInfo[k].showLabel) {
|
||||
if (labelInfo.includes(newInfo[key].dataInfo[k].showLabel)) {
|
||||
useMessage().notifyError(`${newInfo[key].groupName}中第${labelInfo.length + 1}项重复`);
|
||||
that.errorList.push({
|
||||
key: `${key}_${k}`,
|
||||
message: "数据重复"
|
||||
})
|
||||
reject("数据重复")
|
||||
return
|
||||
}else {
|
||||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
console.log("newInfo[key].dataInfo[k]",newInfo[key].dataInfo[k])
|
||||
if (langSort[j] === langList[k].locale) {
|
||||
mergeLabel.push(langList[k].value)
|
||||
}
|
||||
}
|
||||
// 校验重复
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
if (langSort[j] === newInfo[key].dataInfo[k].locale) {
|
||||
if (newInfo[key].dataInfo[k].showLabel) {
|
||||
if (labelInfo.includes(newInfo[key].dataInfo[k].showLabel)) {
|
||||
useMessage().notifyError(`${newInfo[key].groupName}中第${labelInfo.length + 1}项重复`);
|
||||
that.errorList.push({
|
||||
key: `${key}_${k}`,
|
||||
message: "数据重复"
|
||||
})
|
||||
reject("数据重复")
|
||||
return
|
||||
}
|
||||
labelInfo.push(newInfo[key].dataInfo[k].showLabel)
|
||||
}
|
||||
labelInfo.push(newInfo[key].dataInfo[k].showLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1374,7 +1402,7 @@ defineExpose({
|
|||
}
|
||||
|
||||
.error_tip {
|
||||
box-shadow: rgb(255, 0, 0) 0px 0px 2px 1px;
|
||||
box-shadow: rgb(255, 0, 0) 0 0 2px 1px;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
<el-row>
|
||||
<el-col :span="6">
|
||||
<div style="width: 120px;display: flex">
|
||||
<el-checkbox
|
||||
<el-checkbox :disabled="disableInput"
|
||||
v-model="that.configInfo.canChange">
|
||||
<span>允许调整数量</span>
|
||||
</el-checkbox>
|
||||
|
|
|
|||
|
|
@ -1462,6 +1462,7 @@ const handlerGroupList = (cells, isCombo = false, min, max) => {
|
|||
canChange = that.pageConfig.propList[i].canChange === true
|
||||
canInput = that.pageConfig.propList[i].canInput === true
|
||||
langSort = that.pageConfig.propList[i].langSort || []
|
||||
linkChar = that.pageConfig.propList[i].linkChar || ','
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" :disabled="formType=='update'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
|
@ -175,6 +175,7 @@ const formRules = reactive({
|
|||
}
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ importers:
|
|||
diagram-js:
|
||||
specifier: ^12.8.0
|
||||
version: 12.8.1
|
||||
dom-to-image:
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
driver.js:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
|
|
@ -2802,6 +2805,9 @@ packages:
|
|||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
dom-to-image@2.6.0:
|
||||
resolution: {integrity: sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==}
|
||||
|
||||
dom-walk@0.1.2:
|
||||
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
|
||||
|
||||
|
|
@ -7410,7 +7416,7 @@ snapshots:
|
|||
'@types/web-bluetooth': 0.0.16
|
||||
'@vueuse/metadata': 9.13.0
|
||||
'@vueuse/shared': 9.13.0(vue@3.4.21(typescript@5.3.3))
|
||||
vue-demi: 0.14.7(vue@3.4.21(typescript@5.3.3))
|
||||
vue-demi: 0.14.10(vue@3.4.21(typescript@5.3.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
|
@ -7421,14 +7427,14 @@ snapshots:
|
|||
|
||||
'@vueuse/shared@10.9.0(vue@3.4.21(typescript@5.3.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.7(vue@3.4.21(typescript@5.3.3))
|
||||
vue-demi: 0.14.10(vue@3.4.21(typescript@5.3.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/shared@9.13.0(vue@3.4.21(typescript@5.3.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.7(vue@3.4.21(typescript@5.3.3))
|
||||
vue-demi: 0.14.10(vue@3.4.21(typescript@5.3.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
|
@ -8217,6 +8223,8 @@ snapshots:
|
|||
domhandler: 5.0.3
|
||||
entities: 4.5.0
|
||||
|
||||
dom-to-image@2.6.0: {}
|
||||
|
||||
dom-walk@0.1.2: {}
|
||||
|
||||
dom7@3.0.0:
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -12,6 +12,21 @@ export interface SmsLoginVO {
|
|||
code: string
|
||||
}
|
||||
|
||||
export interface MailCodeVO {
|
||||
mail: string
|
||||
scene: number
|
||||
type: number
|
||||
}
|
||||
|
||||
|
||||
export interface MailModifyPwdVO {
|
||||
mail: string
|
||||
password: string
|
||||
checkpassword: string
|
||||
code: string
|
||||
type: number
|
||||
}
|
||||
|
||||
// 登录
|
||||
export const login = (data: UserLoginVO) => {
|
||||
return request.post({ url: '/system/auth/login', data })
|
||||
|
|
@ -47,6 +62,16 @@ export const sendSmsCode = (data: SmsCodeVO) => {
|
|||
return request.post({ url: '/system/auth/send-sms-code', data })
|
||||
}
|
||||
|
||||
//获取邮箱登录验证码
|
||||
export const sendMailCode = (data: MailCodeVO) => {
|
||||
return request.post({ url: '/system/auth/send-mail-code', data })
|
||||
}
|
||||
|
||||
// 邮箱码修改密码
|
||||
export const mailModifyPwd = (data: MailModifyPwdVO) => {
|
||||
return request.post({ url: '/system/auth/mail-modifypwd', data })
|
||||
}
|
||||
|
||||
// 短信验证码登录
|
||||
export const smsLogin = (data: SmsLoginVO) => {
|
||||
return request.post({ url: '/system/auth/sms-login', data })
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<el-form label-width="180px">
|
||||
<el-form label-width="200px">
|
||||
<el-form-item label="Style" v-show="that.draftDesignList.length > 1">
|
||||
<div class="flex ml-3">
|
||||
<div>
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
</div>
|
||||
<div class="w-full" v-if="that.propOrderByList && that.propOrderByList.length > 0">
|
||||
<el-scrollbar height="600px">
|
||||
<el-form label-width="180px">
|
||||
<el-form label-width="200px">
|
||||
<el-form-item
|
||||
v-for="(tmp,orderIndex) in that.propOrderByList"
|
||||
|
||||
|
|
@ -134,8 +134,9 @@
|
|||
:class="{
|
||||
'error_tip': errorItem(tmp.key,i)
|
||||
}" v-if="that.propInfo[tmp.key].canInput">
|
||||
|
||||
<el-autocomplete
|
||||
style="min-width: 200px;width: 60%"
|
||||
style="min-width: 320px;width: 100%"
|
||||
v-model="that.propInfo[tmp.key].dataInfo[i].showLabel"
|
||||
:fetch-suggestions="querySearch"
|
||||
clearable
|
||||
|
|
@ -500,6 +501,7 @@ const changeIconData = (index: number, key: string) => {
|
|||
for (let i = 0; i < infoList.length; i++) {
|
||||
mapping[infoList[i].label] = infoList[i].langMapping;
|
||||
}
|
||||
that.propInfo[key].processType = findProcessTypeIndex(index);
|
||||
// 多语言变量替换
|
||||
const allData = [...that.propInfo[key].dataInfo]
|
||||
const fLang = allData[0].locale;
|
||||
|
|
@ -563,7 +565,7 @@ const findProcessTypeIndex = (index) => {
|
|||
const washingInfoListByType = (type, index = -1) => {
|
||||
const typeIndex = findProcessTypeIndex(index);
|
||||
return that.washingInfoList.filter(item => {
|
||||
if (item.type === type && (typeIndex < 0 || (item.processType === `${typeIndex}`))) {
|
||||
if (`${item.type}` === `${type}` && (typeIndex < 0 || (`${item.processType}` === `${typeIndex}`))) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
|
@ -896,18 +898,17 @@ const checkPropInfo = (info) => {
|
|||
for (let i = 0; i < that.propOrderByList.length; i++) {
|
||||
keys.push(that.propOrderByList[i].key);
|
||||
}
|
||||
|
||||
endloop: for (let i = 0; i < keys.length; i++) {
|
||||
console.log("keys",keys)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
// 合并相同语言的文字
|
||||
let newArr = [];
|
||||
// 每种语言合并成一句话
|
||||
const allLang = findGroupAllLang(key, newInfo2);
|
||||
const firstLang = newInfo[key].dataInfo[0].locale;
|
||||
const linkChar = newInfo[key].linkChar || '';
|
||||
const dataInfoArr = newInfo[key].dataInfo;
|
||||
let langList = newInfo[key].langList || [];
|
||||
console.log("newInfo[key].groupType",newInfo[key])
|
||||
if (newInfo[key].groupType === GroupTypeEnum.RATIO) {
|
||||
|
||||
// 成分比例 都进行固定语言组合
|
||||
// 获取到所以的成分
|
||||
let langSort = newInfo[key].langSort || [];
|
||||
|
|
@ -937,6 +938,8 @@ const checkPropInfo = (info) => {
|
|||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
let allRatio = 0; // 累计 比例
|
||||
const langLabel = [];
|
||||
let linkChar = ","
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
if (langSort[j] === langList[k].locale) {
|
||||
let tmpValue = `${langList[k].value}`.replaceAll('${r}', `${str}`)
|
||||
|
|
@ -946,9 +949,17 @@ const checkPropInfo = (info) => {
|
|||
if (str === null || str === undefined || str === -1) {
|
||||
str = ''
|
||||
}
|
||||
mergeLabel.push(tmpValue)
|
||||
console.log("langList[k]",langList[k])
|
||||
if(langList[k].linkChar && linkChar != ','){
|
||||
linkChar = langList[k]
|
||||
}
|
||||
langLabel.push(tmpValue)
|
||||
}
|
||||
}
|
||||
if(langLabel.length > 0){
|
||||
mergeLabel.push(langLabel.join(linkChar))
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
// 校验
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
|
|
@ -1006,7 +1017,6 @@ const checkPropInfo = (info) => {
|
|||
if (newInfo[kKey].groupName === newInfo[key].parentId) {
|
||||
newKey = kKey;
|
||||
parentStr = newInfo[kKey].dataInfo[0].label + '<br/>'
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1021,24 +1031,28 @@ const checkPropInfo = (info) => {
|
|||
|
||||
// 语言对应自己的自动位置
|
||||
if (newInfo[key].multiLanguage) {
|
||||
const showLabel1 = newInfo[key].dataInfo[0].showLabel
|
||||
if (newInfo[key].groupType === GroupTypeEnum.ICON) {
|
||||
const infoList = washingInfoListByType(newInfo[key].groupType)
|
||||
const infos = newInfo[key].dataInfo;
|
||||
for (let j = 1; j < infos.length; j++) {
|
||||
|
||||
outerLoop: for (let i = 0; i < infoList.length; i++) {
|
||||
const row1 = infoList[i];
|
||||
for (let k = 0; k < row1.langMapping.length; k++) {
|
||||
// 跳过icon 类型,因为没有url
|
||||
if (infos[j].locale === row1.langMapping[k].locale) {
|
||||
infos[j].label = row1.langMapping[k].value
|
||||
break outerLoop;
|
||||
if(`${row1.processType}` === `${ newInfo[key].processType}`){
|
||||
for (let k = 0; k < row1.langMapping.length; k++) {
|
||||
// 跳过icon 类型,因为没有url
|
||||
if (infos[j].locale === row1.langMapping[k].locale && showLabel1 === row1.value) {
|
||||
infos[j].label = row1.langMapping[k].value
|
||||
break outerLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("newArr33",infos)
|
||||
newInfo[key].dataInfo = infos;
|
||||
break;
|
||||
|
||||
}else {
|
||||
const infoList = washingInfoListByType(newInfo[key].groupType)
|
||||
const infos = newInfo[key].dataInfo;
|
||||
|
|
@ -1054,41 +1068,55 @@ const checkPropInfo = (info) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
console.log("newArr33",infos)
|
||||
console.log("newArr99",infos)
|
||||
newInfo[key].dataInfo = infos;
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
// 多种语言组合成一句话
|
||||
// 文本类型处理
|
||||
if (newInfo[key].groupType === GroupTypeEnum.TEXT) {
|
||||
|
||||
let langSort = newInfo[key].langSort || [];
|
||||
if (langSort.length === 0) {
|
||||
langSort = allLang
|
||||
}
|
||||
let mergeLabel = [];
|
||||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
if (langSort[j] === langList[k].locale && newInfo[key].dataInfo[k].showLabel) {
|
||||
mergeLabel.push(langList[k].value)
|
||||
|
||||
if(newInfo[key].canInput){
|
||||
for (let j = 0; j < dataInfoArr.length; j++) {
|
||||
if (dataInfoArr[j].showLabel) {
|
||||
if(mergeLabel.includes(dataInfoArr[j].showLabel)){
|
||||
useMessage().notifyWarning(`${newInfo[key].groupName}中,第${j + 1}项重复${dataInfoArr[j].showLabel}`)
|
||||
}
|
||||
mergeLabel.push(dataInfoArr[j].showLabel)
|
||||
}
|
||||
}
|
||||
// 校验重复
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
if (langSort[j] === newInfo[key].dataInfo[k].locale) {
|
||||
if (newInfo[key].dataInfo[k].showLabel) {
|
||||
if (labelInfo.includes(newInfo[key].dataInfo[k].showLabel)) {
|
||||
useMessage().notifyError(`${newInfo[key].groupName}中第${labelInfo.length + 1}项重复`);
|
||||
that.errorList.push({
|
||||
key: `${key}_${k}`,
|
||||
message: "数据重复"
|
||||
})
|
||||
reject("数据重复")
|
||||
return
|
||||
}else {
|
||||
for (let j = 0; j < langSort.length; j++) {
|
||||
const labelInfo = []
|
||||
for (let k = 0; k < langList.length; k++) {
|
||||
console.log("newInfo[key].dataInfo[k]",newInfo[key].dataInfo[k])
|
||||
if (langSort[j] === langList[k].locale) {
|
||||
mergeLabel.push(langList[k].value)
|
||||
}
|
||||
}
|
||||
// 校验重复
|
||||
for (let k = 0; k < dataInfoArr.length; k++) {
|
||||
if (langSort[j] === newInfo[key].dataInfo[k].locale) {
|
||||
if (newInfo[key].dataInfo[k].showLabel) {
|
||||
if (labelInfo.includes(newInfo[key].dataInfo[k].showLabel)) {
|
||||
useMessage().notifyError(`${newInfo[key].groupName}中第${labelInfo.length + 1}项重复`);
|
||||
that.errorList.push({
|
||||
key: `${key}_${k}`,
|
||||
message: "数据重复"
|
||||
})
|
||||
reject("数据重复")
|
||||
return
|
||||
}
|
||||
labelInfo.push(newInfo[key].dataInfo[k].showLabel)
|
||||
}
|
||||
labelInfo.push(newInfo[key].dataInfo[k].showLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1374,7 +1402,7 @@ defineExpose({
|
|||
}
|
||||
|
||||
.error_tip {
|
||||
box-shadow: rgb(255, 0, 0) 0px 0px 2px 1px;
|
||||
box-shadow: rgb(255, 0, 0) 0 0 2px 1px;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
<el-row>
|
||||
<el-col :span="6">
|
||||
<div style="width: 120px;display: flex">
|
||||
<el-checkbox
|
||||
<el-checkbox :disabled="disableInput"
|
||||
v-model="that.configInfo.canChange">
|
||||
<span>允许调整数量</span>
|
||||
</el-checkbox>
|
||||
|
|
|
|||
|
|
@ -1462,6 +1462,7 @@ const handlerGroupList = (cells, isCombo = false, min, max) => {
|
|||
canChange = that.pageConfig.propList[i].canChange === true
|
||||
canInput = that.pageConfig.propList[i].canInput === true
|
||||
langSort = that.pageConfig.propList[i].langSort || []
|
||||
linkChar = that.pageConfig.propList[i].linkChar || ','
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export default {
|
|||
},
|
||||
login: {
|
||||
welcome: 'Welcome to the system',
|
||||
message: 'Backstage management system',
|
||||
message: '',
|
||||
tenantname: 'TenantName',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
|
|
@ -133,14 +133,20 @@ export default {
|
|||
codePlaceholder: 'Please Enter Verification Code',
|
||||
mobileTitle: 'Mobile sign in',
|
||||
mobileNumber: 'Mobile Number',
|
||||
mobileNumberPlaceholder: 'Plaease Enter Mobile Number',
|
||||
mobileNumberPlaceholder: 'Plaease Enter Mobile Code',
|
||||
emailNumberPlaceholder: 'Plaease Enter Email Code',
|
||||
backLogin: 'back',
|
||||
getSmsCode: 'Get SMS Code',
|
||||
getMailCode: 'Get Mail Code',
|
||||
btnMobile: 'Mobile sign in',
|
||||
btnQRCode: 'QR code sign in',
|
||||
qrcode: 'Scan the QR code to log in',
|
||||
btnRegister: 'Sign up',
|
||||
SmsSendMsg: 'code has been sent'
|
||||
SmsSendMsg: 'code has been sent',
|
||||
pwdResetSuccess: 'password reset success'
|
||||
},
|
||||
forgetpassword: {
|
||||
resetpwd: 'reset pwd',
|
||||
},
|
||||
captcha: {
|
||||
verification: 'Please complete security verification',
|
||||
|
|
|
|||
|
|
@ -134,13 +134,19 @@ export default {
|
|||
mobileTitle: '手机登录',
|
||||
mobileNumber: '手机号码',
|
||||
mobileNumberPlaceholder: '请输入手机号码',
|
||||
emailNumberPlaceholder: '请输入邮箱号码',
|
||||
backLogin: '返回',
|
||||
getSmsCode: '获取验证码',
|
||||
getMailCode: '获取验证码',
|
||||
btnMobile: '手机登录',
|
||||
btnQRCode: '二维码登录',
|
||||
qrcode: '扫描二维码登录',
|
||||
btnRegister: '注册',
|
||||
SmsSendMsg: '验证码已发送'
|
||||
SmsSendMsg: '验证码已发送',
|
||||
pwdResetSuccess: '密码重置成功'
|
||||
},
|
||||
forgetpassword: {
|
||||
resetpwd: '重置密码',
|
||||
},
|
||||
captcha: {
|
||||
verification: '请完成安全验证',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<!-- <div
|
||||
<div
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
>
|
||||
<div class="flex items-center at-2xl:hidden at-xl:hidden">
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
<ThemeSwitch />
|
||||
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
|
|
@ -56,6 +56,8 @@
|
|||
<RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 三方登录 -->
|
||||
<SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 忘记密码 -->
|
||||
<ForgetPasswordForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
|
@ -70,7 +72,7 @@ import { useAppStore } from '@/store/modules/app'
|
|||
import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
|
||||
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
||||
|
||||
import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components'
|
||||
import { LoginForm, MobileForm, ForgetPasswordForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components'
|
||||
|
||||
defineOptions({ name: 'Login' })
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
<template>
|
||||
<el-form
|
||||
v-show="getShow"
|
||||
ref="formSmsLogin"
|
||||
:model="loginData.loginForm"
|
||||
:rules="rules"
|
||||
class="login-form"
|
||||
label-position="top"
|
||||
label-width="120px"
|
||||
size="large"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<!-- 租户名 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
:placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse"
|
||||
type="primary"
|
||||
link
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>-->
|
||||
<!-- 手机号 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="mailNumber">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.mailNumber"
|
||||
:placeholder="t('login.emailNumberPlaceholder')"
|
||||
:prefix-icon="iconCellemail"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 密码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
disableautocomplete
|
||||
autocomplete="off"
|
||||
show-password="true"
|
||||
v-model="loginData.loginForm.password"
|
||||
:placeholder="t('login.password')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
||||
<!-- 确认密码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="checkPassword">
|
||||
<el-input
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
disableautocomplete
|
||||
show-password="true"
|
||||
v-model="loginData.loginForm.checkPassword"
|
||||
:placeholder="t('login.checkPassword')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 验证码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="code">
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="24">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.code"
|
||||
:placeholder="t('login.codePlaceholder')"
|
||||
:prefix-icon="iconCircleCheck"
|
||||
>
|
||||
<!-- <el-button class="w-[100%]"> -->
|
||||
<template #append>
|
||||
<span
|
||||
v-if="mailCodeTimer <= 0"
|
||||
class="getMobileCode"
|
||||
style="cursor: pointer"
|
||||
@click="getMailCode"
|
||||
>
|
||||
{{ t('login.getMailCode') }}
|
||||
</span>
|
||||
<span v-if="mailCodeTimer > 0" class="getMobileCode" style="cursor: pointer">
|
||||
{{ mailCodeTimer }}秒后可重新获取
|
||||
</span>
|
||||
</template>
|
||||
</el-input>
|
||||
<!-- </el-button> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 登录按钮 / 返回按钮 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('forgetpassword.resetpwd')"
|
||||
class="w-[100%]"
|
||||
type="primary"
|
||||
@click="modifyPwdIn()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.backLogin')"
|
||||
class="w-[100%]"
|
||||
@click="handleBackLogin()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
|
||||
import { setTenantId, setToken } from '@/utils/auth'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { getTenantIdByName, sendMailCode, mailModifyPwd } from '@/api/login'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
|
||||
import { ElLoading } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'ForgetPasswordForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const permissionStore = usePermissionStore()
|
||||
const { currentRoute, push } = useRouter()
|
||||
const formSmsLogin = ref()
|
||||
const loginLoading = ref(false)
|
||||
const iconHouse = useIcon({ icon: 'ep:house' })
|
||||
const iconCellphone = useIcon({ icon: 'ep:cellphone' })
|
||||
const iconCellemail= useIcon({ icon: 'ep:cellphone' })
|
||||
const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
|
||||
const { validForm } = useFormValid(formSmsLogin)
|
||||
const { handleBackLogin, getLoginState } = useLoginState()
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
||||
|
||||
const rules = {
|
||||
tenantName: [required],
|
||||
mobileNumber: [required],
|
||||
code: [required]
|
||||
}
|
||||
const loginData = reactive({
|
||||
codeImg: '',
|
||||
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
|
||||
token: '',
|
||||
loading: {
|
||||
modifyPwdIn: false
|
||||
},
|
||||
loginForm: {
|
||||
uuid: '',
|
||||
tenantName: '芋道源码',
|
||||
mobileNumber: '',
|
||||
code: ''
|
||||
}
|
||||
})
|
||||
const mailVO = reactive({
|
||||
mailCode: {
|
||||
mail: '',
|
||||
scene: 4,
|
||||
type: 999999
|
||||
},
|
||||
loginMail: {
|
||||
mail: '',
|
||||
code: '',
|
||||
type: 999999
|
||||
}
|
||||
})
|
||||
const mailCodeTimer = ref(0)
|
||||
const redirect = ref<string>('')
|
||||
const getMailCode = async () => {
|
||||
await getTenantId()
|
||||
mailVO.mailCode.mail = loginData.loginForm.mailNumber
|
||||
await sendMailCode(mailVO.mailCode).then(async () => {
|
||||
message.success(t('login.SmsSendMsg'))
|
||||
// 设置倒计时
|
||||
mailCodeTimer.value = 60
|
||||
let msgTimer = setInterval(() => {
|
||||
mailCodeTimer.value = mailCodeTimer.value - 1
|
||||
if (mailCodeTimer.value <= 0) {
|
||||
clearInterval(msgTimer)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
(route: RouteLocationNormalizedLoaded) => {
|
||||
redirect.value = route?.query?.redirect as string
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
// 获取租户 ID
|
||||
const getTenantId = async () => {
|
||||
if (loginData.tenantEnable === 'true') {
|
||||
const res = await getTenantIdByName(loginData.loginForm.tenantName)
|
||||
setTenantId(res)
|
||||
}
|
||||
}
|
||||
// 登录
|
||||
const modifyPwdIn = async () => {
|
||||
await getTenantId()
|
||||
const data = await validForm()
|
||||
if (!data) return
|
||||
/* ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在加载系统中...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})*/
|
||||
loginLoading.value = true
|
||||
mailVO.loginMail.mail = loginData.loginForm.mailNumber
|
||||
mailVO.loginMail.code = loginData.loginForm.code
|
||||
mailVO.loginMail.password = loginData.loginForm.password
|
||||
mailVO.loginMail.checkpassword = loginData.loginForm.checkPassword
|
||||
await mailModifyPwd(mailVO.loginMail)
|
||||
.then(async (res) => {
|
||||
/* setToken(res)
|
||||
if (!redirect.value) {
|
||||
redirect.value = '/'
|
||||
}
|
||||
push({ path: redirect.value || permissionStore.addRouters[0].path })*/
|
||||
await message.alertSuccess(t('login.pwdResetSuccess'))
|
||||
handleBackLogin()
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loginLoading.value = false
|
||||
setTimeout(() => {
|
||||
const loadingInstance = ElLoading.service()
|
||||
loadingInstance.close()
|
||||
}, 400)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.anticon) {
|
||||
&:hover {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.smsbtn {
|
||||
margin-top: 33px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -58,9 +58,9 @@
|
|||
{{ t('login.remember') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
<!-- <el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link>
|
||||
</el-col>-->
|
||||
<el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">{{ t('login.forgetPassword') }}</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import LoginForm from './LoginForm.vue'
|
||||
import MobileForm from './MobileForm.vue'
|
||||
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import RegisterForm from './RegisterForm.vue'
|
||||
import QrCodeForm from './QrCodeForm.vue'
|
||||
import SSOLoginVue from './SSOLogin.vue'
|
||||
|
||||
export { LoginForm, MobileForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue }
|
||||
export { LoginForm, MobileForm, ForgetPasswordForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue }
|
||||
|
|
|
|||