移动端登录
This commit is contained in:
@@ -59,6 +59,12 @@
|
||||
<version>2.9.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.antherd</groupId>
|
||||
<artifactId>sm-crypto</artifactId>
|
||||
<version>0.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xingyuv</groupId>
|
||||
<artifactId>captcha-plus</artifactId>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package io.v.nutz.base.utils;
|
||||
|
||||
import com.antherd.smcrypto.sm2.Sm2;
|
||||
import com.antherd.smcrypto.sm3.Sm3;
|
||||
import com.antherd.smcrypto.sm4.Sm4;
|
||||
import com.antherd.smcrypto.sm4.Sm4Options;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* 为老兼容老的密码加密方式
|
||||
*
|
||||
* @author wizzer@qq.com
|
||||
*/
|
||||
@Slf4j
|
||||
public class PwdUtil {
|
||||
|
||||
/** 公钥 */
|
||||
private static final String PUBLIC_KEY = "04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54";
|
||||
|
||||
/** 私钥 */
|
||||
private static final String PRIVATE_KEY = "3037723d47292171677ec8bd7dc9af696c7472bc5f251b2cec07e65fdef22e25";
|
||||
|
||||
/** SM4的对称秘钥(生产环境需要改成自己使用的) 16 进制字符串,要求为 128 比特 */
|
||||
private static final String KEY = "0123456789abcdeffedcba9876543210";
|
||||
|
||||
public static String getPassword(String passowrd, String salt) {
|
||||
byte[] bytes = hash(passowrd.getBytes(), salt.getBytes(), 1024);
|
||||
if (bytes != null) {
|
||||
return Lang.fixedHexString(bytes);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static byte[] hash(byte[] bytes, byte[] salt, int hashIterations) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
if (salt != null) {
|
||||
digest.reset();
|
||||
digest.update(salt);
|
||||
}
|
||||
|
||||
byte[] hashed = digest.digest(bytes);
|
||||
int iterations = hashIterations - 1;
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
digest.reset();
|
||||
hashed = digest.digest(hashed);
|
||||
}
|
||||
return hashed;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密方法
|
||||
* 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
|
||||
* @param str 密文
|
||||
* @return 解密后的明文
|
||||
*/
|
||||
public static String doSm2Decrypt(String str) {
|
||||
// 解密
|
||||
return Sm2.doDecrypt(str, PRIVATE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密方法
|
||||
* @param str 待加密数据
|
||||
* @return 加密后的密文
|
||||
*/
|
||||
public static String doSm4CbcEncrypt(String str) {
|
||||
// SM4 加密 cbc模式
|
||||
Sm4Options sm4Options4 = new Sm4Options();
|
||||
sm4Options4.setMode("cbc");
|
||||
sm4Options4.setIv("fedcba98765432100123456789abcdef");
|
||||
return Sm4.encrypt(str, KEY, sm4Options4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密方法
|
||||
* 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
|
||||
* @param str 密文
|
||||
* @return 解密后的明文
|
||||
*/
|
||||
public static String doSm4CbcDecrypt(String str) {
|
||||
// 解密,cbc 模式,输出 utf8 字符串
|
||||
Sm4Options sm4Options8 = new Sm4Options();
|
||||
sm4Options8.setMode("cbc");
|
||||
sm4Options8.setIv("fedcba98765432100123456789abcdef");
|
||||
String docString = Sm4.decrypt(str, KEY, sm4Options8);
|
||||
if ("".equals(docString)) {
|
||||
log.warn(">>> 字段解密失败,返回原文值:{}", str);
|
||||
return str;
|
||||
} else {
|
||||
return docString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 纯签名
|
||||
* @param str 待签名数据
|
||||
* @return 签名结果
|
||||
*/
|
||||
public static String doSignature(String str) {
|
||||
return Sm2.doSignature(str, PRIVATE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过杂凑算法取得hash值,用于做数据完整性保护
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return hash 值
|
||||
*/
|
||||
public static String doHashValue(String str) {
|
||||
return Sm3.sm3(str);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// String pwd = "123456";
|
||||
// String dbPwd = PwdUtil.doHashValue(pwd);
|
||||
// String s = PwdUtil.doHashValue(PwdUtil.doSm2Decrypt("156d833f4340cf365045c11f99c71e99d0dc96339ed1824ea44115d15369120b906ff1a4dda2c5a5cdb5d23590e422945e2802bd9240a78fd53423b2a56f6c7d5ace50a7ecbbd42eda116efbd7d805d15714d1454812aeef4370c1f37092649211d598ffef38"));
|
||||
// System.out.println(dbPwd);
|
||||
// System.out.println(s);
|
||||
|
||||
String pwd = "123456";
|
||||
String dbPwd = PwdUtil.doSm4CbcEncrypt(pwd);
|
||||
System.out.println(dbPwd);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
package io.v.nutz.web.commons.shiro.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import io.v.nutz.base.utils.LoginUtil;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import io.v.nutz.base.utils.PwdUtil;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Slf4j
|
||||
public class EasyCredentialsMatch extends HashedCredentialsMatcher {
|
||||
|
||||
/**
|
||||
@@ -31,12 +33,25 @@ public class EasyCredentialsMatch extends HashedCredentialsMatcher {
|
||||
return true;
|
||||
}
|
||||
|
||||
String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "@dd3s#3618");
|
||||
// 去校验密码
|
||||
PrincipalCollection principals = info.getPrincipals();
|
||||
Sys_user user = BeanUtil.copyProperties(principals.getPrimaryPrincipal(), Sys_user.class);
|
||||
|
||||
if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
|
||||
// 登录时密码
|
||||
String password = String.valueOf(platformCaptchaToken.getPassword());
|
||||
// 用户密码
|
||||
String userPassword = PwdUtil.doSm4CbcDecrypt(user.getPassword());
|
||||
|
||||
if (password.equals(userPassword)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "11111");
|
||||
//
|
||||
// if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
|
||||
Object accountCredentials = getCredentials(info);
|
||||
return equals(tokenHashedCredentials, accountCredentials);
|
||||
|
||||
@@ -2,16 +2,25 @@ package io.v.nutz.zhgh.mobile;
|
||||
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.utils.LoginUtil;
|
||||
import io.v.nutz.base.utils.PwdUtil;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.sys.services.SysUserService;
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaEmptyException;
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaIncorrectException;
|
||||
import io.v.nutz.web.commons.shiro.filter.PlatformAuthenticationFilter;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.*;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.LockedAccountException;
|
||||
import org.apache.shiro.authc.UnknownAccountException;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.apache.shiro.session.SessionException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
@@ -25,8 +34,12 @@ import javax.servlet.http.HttpSession;
|
||||
@Ok("json")
|
||||
public class mobileLoginController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private LoginUtil loginUtil;
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
|
||||
/**
|
||||
* 登录页
|
||||
@@ -104,4 +117,26 @@ public class mobileLoginController {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@RequiresAuthentication
|
||||
public Object updatePwd(@Param("sourcePwd") String sourcePwd, @Param("newPwd") String newPwd) {
|
||||
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", ShiroUtil.getUserId()));
|
||||
String userSourcePwd = PwdUtil.doSm4CbcDecrypt(user.getPassword());
|
||||
|
||||
if (!userSourcePwd.equals(sourcePwd)) {
|
||||
return Result.error("原密码错误");
|
||||
}
|
||||
|
||||
if (userSourcePwd.equals(newPwd)) {
|
||||
return Result.error("新密码不能与原密码相同");
|
||||
}
|
||||
|
||||
String pwd = PwdUtil.doSm4CbcEncrypt(newPwd);
|
||||
dao.update(Sys_user.class, Chain.make("password", pwd), Cnd.where("id", "=", ShiroUtil.getUserId()));
|
||||
|
||||
sysUserService.deleteCacheAndUpdate(ShiroUtil.getUserId());
|
||||
return Result.success("修改成功");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +164,128 @@ layout("/mobile/platform.html"){
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.pwd_popup {
|
||||
width: 96%;
|
||||
height: auto;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.menu_item_name {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.card {
|
||||
/*border: 1px solid #007bff;*/
|
||||
/*background: #fff;*/
|
||||
border-radius: 8px;
|
||||
/*box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);*/
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
.card-content {
|
||||
padding: 30px;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
font-size: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
.form-group {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
font-size: 16px;
|
||||
}
|
||||
input[type="password"],
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.3s;
|
||||
height: 40px;
|
||||
}
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
button {
|
||||
background-color: #e0f0ff;
|
||||
color: #000;
|
||||
border: 1px solid #007bff;
|
||||
padding: 12px;
|
||||
font-weight: bold;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #87c4ed;
|
||||
}
|
||||
.note {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
padding: 30px;
|
||||
}
|
||||
.note ul {
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
.note li {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.card.modify-password {
|
||||
flex: 7;
|
||||
}
|
||||
.card.note {
|
||||
flex: 3;
|
||||
}
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-content {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #007bff;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-content h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 70%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 26px;
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
@@ -182,6 +301,10 @@ layout("/mobile/platform.html"){
|
||||
<!--应用窗格-->
|
||||
<van-row class="first module">
|
||||
<div class="menu" style="border-radius: 10px">
|
||||
<div @click="updatePwd" class="menu_item">
|
||||
<img src="/assets/mobile/img/home/1民主管理.jpg" alt="" class="menu_item_img" />
|
||||
<span class="menu_item_name">修改密码</span>
|
||||
</div>
|
||||
<div @click="switchModuleMenus(item)" v-for="item in moduleMenus" class="menu_item">
|
||||
<img :src="CREATE_PREVIEW_URL(item.iconClass)" alt="" class="menu_item_img" />
|
||||
<span class="menu_item_name">{{item.moduleName}}</span>
|
||||
@@ -318,6 +441,39 @@ layout("/mobile/platform.html"){
|
||||
<mobile-sign-points></mobile-sign-points>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="updatePwdShow" class="pwd_popup">
|
||||
<div class="card modify-password">
|
||||
<div class="card-content">
|
||||
<h2>修改密码</h2>
|
||||
<form id="password-form">
|
||||
<div class="form-group">
|
||||
<label for="current-password">当前密码</label>
|
||||
<input type="password" id="current-password" name="current-password" maxlength="20" v-model="formData.sourcePwd" />
|
||||
<span class="toggle-password" data-target="current-password">🔒</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-password">新密码</label>
|
||||
<input type="password" id="new-password" name="new-password" maxlength="20" v-model="formData.newPwd" @blur="checkPwd" />
|
||||
<span class="toggle-password" data-target="new-password">🔒</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm-password">确认新密码</label>
|
||||
<input
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
maxlength="20"
|
||||
v-model="formData.confirmPwd"
|
||||
@blur="checkConfirmPwd"
|
||||
/>
|
||||
<span class="toggle-password" data-target="confirm-password">🔒</span>
|
||||
</div>
|
||||
</form>
|
||||
<button @click="updatePwdSubmit">提交</button>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-tabbar v-model="active" @change="tabbrChange">
|
||||
<van-tabbar-item icon="home-o" name="home">首页</van-tabbar-item>
|
||||
<van-tabbar-item :dot="needCount" icon="envelop-o" name="handle">我的办理</van-tabbar-item>
|
||||
@@ -353,10 +509,63 @@ layout("/mobile/platform.html"){
|
||||
integralViewData: [],
|
||||
totalIntegral: null,
|
||||
yearList: [],
|
||||
monthList: []
|
||||
monthList: [],
|
||||
|
||||
// 修改密码相关
|
||||
updatePwdShow: false,
|
||||
formData: {
|
||||
sourcePwd: "",
|
||||
newPwd: "",
|
||||
confirmPwd: ""
|
||||
},
|
||||
errorInfo: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePwd() {
|
||||
this.updatePwdShow = true
|
||||
},
|
||||
async updatePwdSubmit() {
|
||||
const resp = await $.post("/mobile/login/updatePwd", this.formData)
|
||||
if (resp.code === 0) {
|
||||
this.$toast("修改成功")
|
||||
this.updatePwdShow = false
|
||||
} else {
|
||||
this.$toast(resp.msg)
|
||||
}
|
||||
},
|
||||
checkPwd() {
|
||||
if (!this.formData.newPwd) {
|
||||
this.errorInfo = "请输入新密码"
|
||||
this.$toast("请输入新密码")
|
||||
return
|
||||
}
|
||||
if (this.formData.newPwd.length < 6) {
|
||||
this.errorInfo = "密码长度不能小于6位数"
|
||||
this.$toast("密码长度不能小于6位数")
|
||||
return
|
||||
}
|
||||
this.errorInfo = ""
|
||||
},
|
||||
checkConfirmPwd() {
|
||||
// 检验输入是否大于6位数
|
||||
if (!this.formData.confirmPwd) {
|
||||
this.errorInfo = "请输入确认新密码"
|
||||
this.$toast("请输入确认新密码")
|
||||
return
|
||||
}
|
||||
if (this.formData.confirmPwd.length < 6) {
|
||||
this.errorInfo = "密码长度不能小于6位数"
|
||||
this.$toast("密码长度不能小于6位数")
|
||||
return
|
||||
}
|
||||
if (this.formData.newPwd !== this.formData.confirmPwd) {
|
||||
this.errorInfo = "新密码与确认新密码不一致"
|
||||
this.$toast("新密码与确认新密码不一致")
|
||||
return
|
||||
}
|
||||
this.errorInfo = ""
|
||||
},
|
||||
switchModuleMenus(item) {
|
||||
this.clickMenu = item
|
||||
this.clickIndex = 2
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,396 @@
|
||||
<!--#
|
||||
layout("/mobile/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
html,
|
||||
#app {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.van-nav-bar {
|
||||
background-color: #1e8cde;
|
||||
}
|
||||
|
||||
.van-nav-bar__title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.anchorBL {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 380px;
|
||||
width: 80%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 50px auto auto auto;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.header_v {
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.parm_v {
|
||||
background-color: white;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.input_v {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.van-cell:not(:last-child)::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.van-field {
|
||||
padding: 0;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.bottom_v {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 300px;
|
||||
position: relative; /* 如果之前使用了绝对定位,改为相对定位 */
|
||||
z-index: 1; /* 确保logo在content下方 */
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.verify-img-panel {
|
||||
border: none;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.verify-bar-area {
|
||||
border: none;
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
}
|
||||
|
||||
.van-popup {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
<div v-cloak id="app">
|
||||
<div class="logo">
|
||||
<img style="width: 100%" src="/assets/mobile/img/login.png" />
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<van-row><h1 style="margin: 0 0;color: #1d1b1b;font-family: 'SourceHanSansCN-Heav">Welcome</h1></van-row>
|
||||
<van-row>
|
||||
<span style="color: rgb(160, 160, 160)">欢迎登录智慧工会移动服务平台</span>
|
||||
</van-row>
|
||||
<div class="parm_v">
|
||||
<van-row style="border-bottom: 1px rgb(230, 230, 230) solid !important">
|
||||
<van-col span="2" style="line-height: 50px; text-align: left">
|
||||
<svg
|
||||
t="1592742719420"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2146"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
d="M621.8 601.2c86.8-49.5 146.1-141.9 146.1-249 0-158.9-128.8-287.7-287.7-287.7S192.5 193.3 192.5 352.2c0 107.1 59.2 199.5 146.1 249-150 54.5-259.7 192.7-272.3 358.3h63.9c16.2-178.9 166.9-319.6 350-319.6s333.8 140.7 350 319.6h63.9c-12.6-165.6-122.3-303.8-272.3-358.3z m-365.4-249c0-123.4 100.4-223.7 223.8-223.7S704 228.8 704 352.2 603.6 575.9 480.2 575.9 256.4 475.6 256.4 352.2z"
|
||||
p-id="2147"
|
||||
fill="#8a8a8a"
|
||||
></path>
|
||||
</svg>
|
||||
</van-col>
|
||||
<van-col span="22">
|
||||
<van-field
|
||||
v-model="formData.mobile"
|
||||
type="tel"
|
||||
maxlength="11"
|
||||
:rules="[{ pattern , message: '请输入正确的手机号' }]"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
></van-field>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-row style="border-bottom: 1px rgb(230, 230, 230) solid !important">
|
||||
<van-col span="2" style="line-height: 50px; text-align: left; margin-top: 30px">
|
||||
<svg
|
||||
t="1592742837149"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3554"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
d="M819.221333 417.856l-519.253333 0-0.021333-150.336-0.277333-4.309333c-0.170667-2.453333-0.341333-4.906667-0.341333-7.402667s0.170667-4.970667 0.341333-7.445333l0.298667-13.397333c0.490667-1.408 0.832-2.901333 1.024-4.437333 12.650667-105.962667 102.72-185.877333 209.472-185.877333 106.773333 0 196.842667 79.914667 209.365333 184.085333 0 0 1.770667 50.794667 1.792 55.509333 0.042667 11.754667 9.578667 21.290667 21.333333 21.290667 11.477333 0.832 21.333333-9.493333 21.333333-21.312 0-4.416-1.834667-56.981333-1.962667-58.773333C747.114667 98.069333 638.848 1.984 510.485333 1.984c-125.973333 0-232.618667 92.565333-250.901333 216.448-1.450667 2.88-2.261333 6.122667-2.261333 9.557333l0.021333 13.802667-0.234667 3.690667c-0.234667 3.413333-0.448 6.826667-0.448 10.304s0.213333 6.869333 0.448 10.261333l0.192 151.786667L204.757333 417.834667c-53.44 0-96.938667 44.16-96.938667 98.432l0 412.693333c0 53.013333 42.090667 93.013333 97.898667 93.013333l612.565333 0c55.808 0 97.92-40 97.92-93.013333L916.202667 516.309333C916.181333 462.016 872.682667 417.856 819.221333 417.856zM873.514667 929.002667c0 33.066667-27.797333 50.346667-55.253333 50.346667L205.717333 979.349333c-27.456 0-55.232-17.301333-55.232-50.346667L150.485333 516.309333c0-30.741333 24.341333-55.765333 54.272-55.765333l614.485333 0c29.930667 0 54.293333 25.024 54.293333 55.765333L873.536 929.002667z"
|
||||
p-id="3555"
|
||||
fill="#8a8a8a"
|
||||
></path>
|
||||
</svg>
|
||||
</van-col>
|
||||
<van-col span="22">
|
||||
<van-field v-model="formData.platformCaptcha" placeholder="请输入手机动态码" style="margin-top: 30px" maxlength="6" clearable>
|
||||
<template #button>
|
||||
<div class="login-code" style="margin-top: -20px">
|
||||
<van-button type="info" @click="handleLogin()">{{ codeMsg }}</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</div>
|
||||
|
||||
<div class="bottom_v">
|
||||
<van-button :loading="loading" size="large" color="linear-gradient(to right,#2b97d7,#8bc4ff)" @click="doLogin">登 录</van-button>
|
||||
</div>
|
||||
|
||||
<!--<van-dialog title="请拖动滑块完成拼图" v-model="isShowSliderVerify" @close="refresh" append-to-body>
|
||||
<div id="mpanel" style="margin-top: 50px"></div>
|
||||
</van-dialog>-->
|
||||
|
||||
<van-popup round v-model="isShowSliderVerify" :style="{margin: '0 auto',width: '80%'}" @close="closePopup" safe-area-inset-bottom>
|
||||
<div id="parentPanel"></div>
|
||||
<!-- <div id="mpanel"></div>-->
|
||||
</van-popup>
|
||||
</div>
|
||||
</div>
|
||||
<script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="/assets/bfwrepo/js/bfwone.js"></script>
|
||||
<script>
|
||||
let marker
|
||||
let vue = new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
// 是否禁用按钮
|
||||
codeDisabled: false,
|
||||
// 倒计时秒数
|
||||
countdown: 60,
|
||||
// 按钮上的文字
|
||||
codeMsg: "获取动态码",
|
||||
// 定时器
|
||||
timer: null,
|
||||
isShowSliderVerify: false,
|
||||
loading: false,
|
||||
formData: {
|
||||
mobile: "",
|
||||
password: "",
|
||||
platformKey: "",
|
||||
platformCaptcha: "",
|
||||
// 随机字符串
|
||||
nonceStr: "",
|
||||
// 验证值
|
||||
value: ""
|
||||
},
|
||||
isNull(val) {
|
||||
return !val
|
||||
},
|
||||
pattern: /^1[3-9]\d{9}$/
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"slider-verify": httpVueLoader("/components/captcha/sliderVerify.vue")
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
// 获取父元素
|
||||
var panel = document.getElementById("mpanel")
|
||||
panel.remove()
|
||||
this.isShowSliderVerify = false
|
||||
},
|
||||
async sendCode() {
|
||||
if (this.codeDisabled) {
|
||||
return
|
||||
}
|
||||
if (this.isNull(this.formData.mobile)) {
|
||||
vue.$notify({ type: "warning", message: "请填写手机号" })
|
||||
return
|
||||
}
|
||||
const resp = await this.validMobile()
|
||||
if (resp.code !== 0) {
|
||||
vue.$notify({ type: "warning", message: resp.msg })
|
||||
return
|
||||
}
|
||||
|
||||
/*发送验证码功能*/
|
||||
//若发送验证码成功,则调用倒计时函数
|
||||
const { code, data } = await $.post("/platform/login/mobileCaptcha", { mobile: this.formData.mobile })
|
||||
if (code === 0) {
|
||||
this.formData.platformKey = data.key
|
||||
vue.$notify({ type: "success", message: data.msg })
|
||||
await this.getCode()
|
||||
} else {
|
||||
vue.$notify({ type: "warning", message: "动态码发送失败,请联系管理员" })
|
||||
}
|
||||
},
|
||||
//验证手机号是否存在
|
||||
async validMobile() {
|
||||
return await $.post("${base!}/platform/login/validMobile", { mobile: this.formData.mobile })
|
||||
},
|
||||
// 获取验证码
|
||||
async getCode() {
|
||||
// 验证码60秒倒计时
|
||||
if (!this.timer) {
|
||||
this.timer = setInterval(() => {
|
||||
if (this.countdown > 0 && this.countdown <= 60) {
|
||||
this.countdown--
|
||||
if (this.countdown !== 0) {
|
||||
this.codeDisabled = true
|
||||
this.codeMsg = "重新发送(" + this.countdown + ")"
|
||||
} else {
|
||||
clearInterval(this.timer)
|
||||
this.codeMsg = "获取动态码"
|
||||
this.countdown = 60
|
||||
this.timer = null
|
||||
this.codeDisabled = false
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
//验证图片滑动
|
||||
async validVerify() {
|
||||
return await $.post("${base!}/platform/login/validVerify", {
|
||||
nonceStr: this.formData.nonceStr,
|
||||
value: this.formData.value
|
||||
})
|
||||
},
|
||||
/* 滑动验证成功*/
|
||||
async onSuccess(captcha) {
|
||||
Object.assign(this.formData, captcha)
|
||||
const resp = await this.validVerify()
|
||||
if (resp.code === 0) {
|
||||
console.log("success")
|
||||
this.isShowSliderVerify = false
|
||||
await this.sendCode()
|
||||
} else {
|
||||
this.$refs.sliderVerify.verifyFailEvent(resp.msg)
|
||||
}
|
||||
},
|
||||
/* 滑动验证失败*/
|
||||
onFail(msg) {
|
||||
vue.$notify({ type: "warning", message: msg || "验证失败,请控制拼图对齐缺口" })
|
||||
},
|
||||
/* 滑动验证异常*/
|
||||
onAgain() {
|
||||
vue.$notify({ type: "warning", message: "滑动操作异常,请重试" })
|
||||
this.$refs.sliderVerify.refresh()
|
||||
},
|
||||
/* 刷新验证码*/
|
||||
refresh() {
|
||||
this.$refs.sliderVerify.refresh()
|
||||
},
|
||||
handleLogin() {
|
||||
if (this.isNull(this.formData.mobile)) {
|
||||
vue.$notify({ type: "warning", message: "请填写手机号" })
|
||||
return
|
||||
}
|
||||
this.isShowSliderVerify = true
|
||||
|
||||
const self = this
|
||||
this.$nextTick(() => {
|
||||
// 获取父元素
|
||||
var parentPanel = document.getElementById("parentPanel")
|
||||
|
||||
// 创建新的子元素
|
||||
var childDiv = document.createElement("div")
|
||||
|
||||
// 给子元素设置ID
|
||||
childDiv.id = "mpanel"
|
||||
|
||||
// 将子元素添加到父元素中
|
||||
parentPanel.appendChild(childDiv)
|
||||
|
||||
use(["verify", "verify"], function () {
|
||||
$("#mpanel").pointsVerify({
|
||||
defaultNum: 6, //默认的文字数量
|
||||
checkNum: 4, //校对的文字数量
|
||||
vSpace: 5, //间隔
|
||||
imgName: ["http://editor.bfw.wiki/bfwrepo/image/sample1.png", "http://editor.bfw.wiki/bfwrepo/image/sample2.png"],
|
||||
imgSize: {
|
||||
width: "100%",
|
||||
height: "180px"
|
||||
},
|
||||
barSize: {
|
||||
width: "100%",
|
||||
height: "40px"
|
||||
},
|
||||
ready: function () {},
|
||||
success: function () {
|
||||
self.sendCode()
|
||||
self.isShowSliderVerify = false
|
||||
},
|
||||
error: function () {
|
||||
vue.$notify({ type: "warning", message: "验证失败,请重试" })
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
doLogin() {
|
||||
let self = this
|
||||
if (this.isNull(this.formData.mobile)) {
|
||||
vue.$notify({ type: "warning", message: "请填写手机号" })
|
||||
return
|
||||
}
|
||||
if (this.isNull(this.formData.platformCaptcha)) {
|
||||
vue.$notify({ type: "warning", message: "请填写手机动态码" })
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.formData.loginType = "SMS_CODE"
|
||||
$.post(
|
||||
"/mobile/login/doLogin",
|
||||
this.formData,
|
||||
function (d) {
|
||||
self.loading = false
|
||||
if (d.code === 0) {
|
||||
vue.$notify({ type: "success", message: d.msg })
|
||||
setTimeout(() => {
|
||||
location.href = "/mobile/index"
|
||||
}, 500)
|
||||
} else {
|
||||
vue.$notify({ type: "danger", message: d.msg })
|
||||
self.isShowSliderVerify = false
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user