10 changed files with 401 additions and 337 deletions
@ -1,266 +1,282 @@ |
|||||
<template> |
<template> |
||||
<view class="login-bg"> |
<view class="login-bg"> |
||||
<view class="login-welcome"> |
<view class="login-welcome"> |
||||
<image |
<image class="login-bg-img" src="../../static/img/login-top.png" mode="aspectFill"></image> |
||||
class="login-bg-img" |
<view class="login-welcome-content"> |
||||
src="../../static/img/login-top.png" |
<text class="login-title">您好!</text> |
||||
mode="aspectFill" |
<text class="login-title">欢迎使用</text> |
||||
></image> |
</view> |
||||
<view class="login-welcome-content"> |
</view> |
||||
<text class="login-title">您好!</text> |
<view class="login-form-box"> |
||||
<text class="login-title">欢迎使用</text> |
<view class="login-form-item"> |
||||
</view> |
<text class="login-label">用户名</text> |
||||
</view> |
<u-input v-model="form.username" placeholder="请输入用户名" type="text" border="none" shape="circle" |
||||
<view class="login-form-box"> |
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" /> |
||||
<view class="login-form-item"> |
</view> |
||||
<text class="login-label">用户名</text> |
<view class="login-form-item"> |
||||
<u-input |
<text class="login-label">密码</text> |
||||
v-model="form.username" |
<u-input v-model="form.password" placeholder="请输入密码" type="password" border="none" shape="circle" |
||||
placeholder="请输入用户名" |
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" /> |
||||
type="text" |
</view> |
||||
border="none" |
<view class="login-form-item captcha-row"> |
||||
shape="circle" |
<text class="login-label">验证码</text> |
||||
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" |
<view class="captcha-flex"> |
||||
/> |
<u-input v-model="form.code" placeholder="验证码" border="none" shape="circle" |
||||
</view> |
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" /> |
||||
<view class="login-form-item"> |
<image :src="codeUrl" class="captcha-img" mode="aspectFit" @click="getCaptcha" /> |
||||
<text class="login-label">密码</text> |
</view> |
||||
<u-input |
</view> |
||||
v-model="form.password" |
</view> |
||||
placeholder="请输入密码" |
<view class="login-btn-box"> |
||||
type="password" |
<u-button :hairline="false" shape="circle" :custom-style="btnStyle" @click="handleLogin" |
||||
border="none" |
color="#fff">登录</u-button> |
||||
shape="circle" |
</view> |
||||
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" |
</view> |
||||
/> |
|
||||
</view> |
|
||||
<view class="login-form-item captcha-row"> |
|
||||
<text class="login-label">验证码</text> |
|
||||
<view class="captcha-flex"> |
|
||||
<u-input |
|
||||
v-model="form.code" |
|
||||
placeholder="验证码" |
|
||||
border="none" |
|
||||
shape="circle" |
|
||||
custom-style="background:#f6f6f6;padding:16rpx 0;padding-left:16px;" |
|
||||
/> |
|
||||
<image |
|
||||
:src="codeUrl" |
|
||||
class="captcha-img" |
|
||||
mode="aspectFit" |
|
||||
@click="getCaptcha" |
|
||||
/> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="login-btn-box"> |
|
||||
<u-button |
|
||||
:hairline="false" |
|
||||
shape="circle" |
|
||||
:custom-style="btnStyle" |
|
||||
@click="handleLogin" |
|
||||
color="#fff" |
|
||||
>登录</u-button |
|
||||
> |
|
||||
</view> |
|
||||
</view> |
|
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import { login, getCodeImg } from "../api"; |
import { |
||||
import { encrypt, decrypt } from "../../utils/jsencrypt"; |
login, |
||||
export default { |
getCodeImg |
||||
data() { |
} from "../api"; |
||||
return { |
import { |
||||
form: { |
encrypt, |
||||
username: "",//admin |
decrypt |
||||
password: "",//!Aa12345 |
} from "../../utils/jsencrypt"; |
||||
code: "", |
export default { |
||||
uuid: "", |
data() { |
||||
rememberMe: false, |
return { |
||||
}, |
form: { |
||||
loading: false, |
username: "", //admin |
||||
loginFailCounts: {}, // 记录失败次数 |
password: "", //!Aa12345 |
||||
accountLockTimes: {}, // 记录锁定时间 |
code: "", |
||||
codeUrl: "", // 验证码图片base64或url |
uuid: "", |
||||
btnStyle: { |
rememberMe: false, |
||||
width: "60%", |
}, |
||||
height: "44px", |
loading: false, |
||||
fontSize: "18px", |
loginFailCounts: {}, // 记录失败次数 |
||||
margin: "50px", |
accountLockTimes: {}, // 记录锁定时间 |
||||
marginTop: "200rpx", |
codeUrl: "", // 验证码图片base64或url |
||||
background: "linear-gradient(90deg, #0DC6C6 0%, #13C2C2 100%)", |
btnStyle: { |
||||
}, |
width: "60%", |
||||
}; |
height: "44px", |
||||
}, |
fontSize: "18px", |
||||
methods: { |
margin: "50px", |
||||
async getCaptcha() { |
marginTop: "200rpx", |
||||
const res = await getCodeImg(); |
background: "linear-gradient(90deg, #0DC6C6 0%, #13C2C2 100%)", |
||||
this.captchaEnabled = |
}, |
||||
res.captchaEnabled === undefined ? true : res.captchaEnabled; |
}; |
||||
if (this.captchaEnabled) { |
}, |
||||
this.codeUrl = "data:image/gif;base64," + res.img; |
methods: { |
||||
this.form.uuid = res.uuid; |
async getCaptcha() { |
||||
} |
const res = await getCodeImg(); |
||||
}, |
this.captchaEnabled = |
||||
async handleLogin() { |
res.captchaEnabled === undefined ? true : res.captchaEnabled; |
||||
const username = String(this.form.username); // 强制转为字符串 |
if (this.captchaEnabled) { |
||||
// 1. 检查锁定 |
this.codeUrl = "data:image/gif;base64," + res.img; |
||||
const lockInfo = JSON.parse( |
this.form.uuid = res.uuid; |
||||
uni.getStorageSync(`lock_${username}`) || "{}" |
} |
||||
); |
}, |
||||
if (lockInfo.lockEndTime) { |
async handleLogin() { |
||||
const currentTime = new Date().getTime(); |
// 调用uni.login方法获取code |
||||
if (currentTime < lockInfo.lockEndTime) { |
uni.login({ |
||||
const remainingMinutes = Math.ceil( |
provider: 'weixin', |
||||
(lockInfo.lockEndTime - currentTime) / (1000 * 60) |
success: function(loginRes) { |
||||
); |
// 打印获取到的code |
||||
uni.showToast({ |
console.log('获取的用户code是:' + loginRes.code); |
||||
title: `账号被锁定,请${remainingMinutes}分钟后再试`, |
// uni.request({ |
||||
icon: "none", |
// url: '', |
||||
}); |
// method: 'POST' |
||||
return; |
// }).then(res => { |
||||
} else { |
// console.log(res, 'res') |
||||
uni.removeStorageSync(`lock_${username}`); |
// }) |
||||
} |
}, |
||||
} |
fail: function(error) { |
||||
// 2. 校验表单 |
// 登录失败的回调 |
||||
if (!this.form.username || !this.form.password || !this.form.code) { |
console.error('登录失败:', error); |
||||
uni.showToast({ title: "请填写完整信息", icon: "none" }); |
} |
||||
return; |
}); |
||||
} |
const username = String(this.form.username); // 强制转为字符串 |
||||
this.loading = true; |
// 1. 检查锁定 |
||||
// 3. 记住我 |
const lockInfo = JSON.parse( |
||||
if (this.form.rememberMe) { |
uni.getStorageSync(`lock_${username}`) || "{}" |
||||
uni.setStorageSync("username", this.form.username); |
); |
||||
uni.setStorageSync("password", encrypt(this.form.password)); // 如需加密可加密 |
if (lockInfo.lockEndTime) { |
||||
uni.setStorageSync("rememberMe", true); |
const currentTime = new Date().getTime(); |
||||
} else { |
if (currentTime < lockInfo.lockEndTime) { |
||||
uni.removeStorageSync("username"); |
const remainingMinutes = Math.ceil( |
||||
uni.removeStorageSync("password"); |
(lockInfo.lockEndTime - currentTime) / (1000 * 60) |
||||
uni.removeStorageSync("rememberMe"); |
); |
||||
} |
uni.showToast({ |
||||
const res = await login({ |
title: `账号被锁定,请${remainingMinutes}分钟后再试`, |
||||
username: String(this.form.username), |
icon: "none", |
||||
password: encrypt(this.form.password), // 如需加密可加密 |
}); |
||||
code: this.form.code, |
return; |
||||
uuid: this.form.uuid, |
} else { |
||||
client: "gypc", |
uni.removeStorageSync(`lock_${username}`); |
||||
}); |
} |
||||
this.loading = false; |
} |
||||
console.log(res); |
// 2. 校验表单 |
||||
|
if (!this.form.username || !this.form.password || !this.form.code) { |
||||
if (res.code === 200) { |
uni.showToast({ |
||||
this.loginFailCounts[username] = 0; |
title: "请填写完整信息", |
||||
uni.setStorageSync("token", res.token); |
icon: "none" |
||||
uni.switchTab({ url: "/pages/tabBar/work/index" }); |
}); |
||||
} else { |
return; |
||||
uni.showToast({ title: res.msg, icon: "none" }); |
} |
||||
// 失败计数 |
this.loading = true; |
||||
this.loginFailCounts[username] = |
// 3. 记住我 |
||||
(this.loginFailCounts[username] || 0) + 1; |
if (this.form.rememberMe) { |
||||
if (this.loginFailCounts[username] >= 5) { |
uni.setStorageSync("username", this.form.username); |
||||
const lockEndTime = new Date().getTime() + 10 * 60 * 1000; |
uni.setStorageSync("password", encrypt(this.form.password)); // 如需加密可加密 |
||||
this.accountLockTimes[username] = lockEndTime; |
uni.setStorageSync("rememberMe", true); |
||||
uni.setStorageSync( |
} else { |
||||
`lock_${username}`, |
uni.removeStorageSync("username"); |
||||
JSON.stringify({ lockEndTime, username }) |
uni.removeStorageSync("password"); |
||||
); |
uni.removeStorageSync("rememberMe"); |
||||
uni.showToast({ |
} |
||||
title: "账号登录失败次数过多,已锁定10分钟", |
const res = await login({ |
||||
icon: "none", |
username: String(this.form.username), |
||||
}); |
password: encrypt(this.form.password), // 如需加密可加密 |
||||
} else { |
code: this.form.code, |
||||
uni.showToast({ title: res[1].data.msg || "登录失败", icon: "none" }); |
uuid: this.form.uuid, |
||||
} |
client: "gypc", |
||||
this.getCaptcha(); |
}); |
||||
} |
this.loading = false; |
||||
}, |
console.log(res); |
||||
}, |
|
||||
mounted() { |
if (res.code === 200) { |
||||
this.getCaptcha(); |
this.loginFailCounts[username] = 0; |
||||
}, |
uni.setStorageSync("token", res.token); |
||||
}; |
uni.switchTab({ |
||||
|
url: "/pages/tabBar/work/index" |
||||
|
}); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg, |
||||
|
icon: "none" |
||||
|
}); |
||||
|
// 失败计数 |
||||
|
this.loginFailCounts[username] = |
||||
|
(this.loginFailCounts[username] || 0) + 1; |
||||
|
if (this.loginFailCounts[username] >= 5) { |
||||
|
const lockEndTime = new Date().getTime() + 10 * 60 * 1000; |
||||
|
this.accountLockTimes[username] = lockEndTime; |
||||
|
uni.setStorageSync( |
||||
|
`lock_${username}`, |
||||
|
JSON.stringify({ |
||||
|
lockEndTime, |
||||
|
username |
||||
|
}) |
||||
|
); |
||||
|
uni.showToast({ |
||||
|
title: "账号登录失败次数过多,已锁定10分钟", |
||||
|
icon: "none", |
||||
|
}); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res[1].data.msg || "登录失败", |
||||
|
icon: "none" |
||||
|
}); |
||||
|
} |
||||
|
this.getCaptcha(); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.getCaptcha(); |
||||
|
}, |
||||
|
}; |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped> |
<style scoped> |
||||
.login-bg { |
.login-bg { |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
align-items: center; |
align-items: center; |
||||
background: #f7fafd; |
background: #f7fafd; |
||||
height: 100vh; |
height: 100vh; |
||||
} |
} |
||||
.login-welcome { |
|
||||
position: relative; |
.login-welcome { |
||||
width: 100vw; |
position: relative; |
||||
padding-top: 244rpx; |
width: 100vw; |
||||
overflow: hidden; |
padding-top: 244rpx; |
||||
} |
overflow: hidden; |
||||
.login-bg-img { |
} |
||||
position: absolute; |
|
||||
left: 0; |
.login-bg-img { |
||||
top: 0; |
position: absolute; |
||||
width: 100vw; |
left: 0; |
||||
height: 526px; |
top: 0; |
||||
z-index: 1; |
width: 100vw; |
||||
} |
height: 526px; |
||||
.login-welcome-content { |
z-index: 1; |
||||
position: relative; |
} |
||||
z-index: 2; |
|
||||
width: 100%; |
.login-welcome-content { |
||||
height: 100%; |
position: relative; |
||||
display: flex; |
z-index: 2; |
||||
flex-direction: column; |
width: 100%; |
||||
justify-content: center; |
height: 100%; |
||||
padding-left: 60rpx; |
display: flex; |
||||
margin-bottom: 70rpx; |
flex-direction: column; |
||||
} |
justify-content: center; |
||||
.login-title { |
padding-left: 60rpx; |
||||
font-size: 36rpx; |
margin-bottom: 70rpx; |
||||
font-weight: bold; |
} |
||||
color: #222; |
|
||||
margin-bottom: 10rpx; |
.login-title { |
||||
} |
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #222; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.login-form-box { |
||||
|
width: 85%; |
||||
|
background: #fff; |
||||
|
border-radius: 24rpx; |
||||
|
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06); |
||||
|
padding: 40rpx 30rpx 30rpx 30rpx; |
||||
|
margin-bottom: -46rpx; |
||||
|
} |
||||
|
|
||||
|
.login-form-item { |
||||
|
margin-bottom: 36rpx; |
||||
|
} |
||||
|
|
||||
|
.login-label { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #222; |
||||
|
margin-bottom: 12rpx; |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.login-btn-box { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.captcha-row { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.captcha-flex { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
.login-form-box { |
.captcha-img { |
||||
width: 85%; |
width: 160rpx; |
||||
background: #fff; |
height: 60rpx; |
||||
border-radius: 24rpx; |
margin-left: 16rpx; |
||||
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06); |
border: 1rpx solid #e0e0e0; |
||||
padding: 40rpx 30rpx 30rpx 30rpx; |
border-radius: 8rpx; |
||||
margin-bottom: -46rpx; |
background: #fff; |
||||
} |
box-sizing: border-box; |
||||
.login-form-item { |
} |
||||
margin-bottom: 36rpx; |
</style> |
||||
} |
|
||||
.login-label { |
|
||||
font-size: 28rpx; |
|
||||
font-weight: bold; |
|
||||
color: #222; |
|
||||
margin-bottom: 12rpx; |
|
||||
display: block; |
|
||||
} |
|
||||
.login-btn-box { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
.captcha-row { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
.captcha-flex { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
.captcha-img { |
|
||||
width: 160rpx; |
|
||||
height: 60rpx; |
|
||||
margin-left: 16rpx; |
|
||||
border: 1rpx solid #e0e0e0; |
|
||||
border-radius: 8rpx; |
|
||||
background: #fff; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
</style> |
|
Loading…
Reference in new issue