每周五更新
This commit is contained in:
@@ -90,7 +90,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 判断是否已登录(检查localStorage中是否有token)
|
// 判断是否已登录(检查localStorage中是否有token)
|
||||||
isLoggedIn() {
|
isLoggedIn() {
|
||||||
const token = localStorage.getItem('token')
|
const token = JSON.parse(localStorage.getItem('leasToken'))
|
||||||
return !!token // 有token就是已登录,没有就是未登录
|
return !!token // 有token就是已登录,没有就是未登录
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -179,7 +179,7 @@ export default {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 加载用户邮箱
|
* 加载用户邮箱
|
||||||
* 从localStorage读取用户信息,获取邮箱
|
* 从localStorage读取用户信息,获取用户名
|
||||||
*/
|
*/
|
||||||
loadUserEmail() {
|
loadUserEmail() {
|
||||||
try {
|
try {
|
||||||
@@ -187,8 +187,8 @@ export default {
|
|||||||
const userInfoStr = localStorage.getItem('userInfo')
|
const userInfoStr = localStorage.getItem('userInfo')
|
||||||
if (userInfoStr) {
|
if (userInfoStr) {
|
||||||
const userInfo = JSON.parse(userInfoStr)
|
const userInfo = JSON.parse(userInfoStr)
|
||||||
// 获取邮箱,如果没有邮箱就显示用户名
|
// 获取用户名(userName字段),如果没有就显示默认值
|
||||||
this.userEmail = userInfo.email || userInfo.username || '用户'
|
this.userEmail = userInfo.userName || userInfo.email || userInfo.username || '用户'
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('读取用户信息失败:', e)
|
console.error('读取用户信息失败:', e)
|
||||||
@@ -201,7 +201,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
handleLogout() {
|
handleLogout() {
|
||||||
// 清除localStorage中的所有登录信息
|
// 清除localStorage中的所有登录信息
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('leasToken')
|
||||||
localStorage.removeItem('userInfo')
|
localStorage.removeItem('userInfo')
|
||||||
localStorage.removeItem('leasEmail')
|
localStorage.removeItem('leasEmail')
|
||||||
localStorage.removeItem('userId')
|
localStorage.removeItem('userId')
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import store from './store'
|
|||||||
import ElementUI from 'element-ui';
|
import ElementUI from 'element-ui';
|
||||||
import 'element-ui/lib/theme-chalk/index.css';
|
import 'element-ui/lib/theme-chalk/index.css';
|
||||||
// 引入登录信息处理
|
// 引入登录信息处理
|
||||||
import './utils/loginInfo.js';
|
// import './utils/loginInfo.js';
|
||||||
// 全局输入防表情守卫(极简、无侵入)
|
// 全局输入防表情守卫(极简、无侵入)
|
||||||
import { initNoEmojiGuard } from './utils/noEmojiGuard.js';
|
import { initNoEmojiGuard } from './utils/noEmojiGuard.js';
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,16 @@ export const accountRoutes = [
|
|||||||
allAuthority: ['all']
|
allAuthority: ['all']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'seller-funds-flow',
|
||||||
|
name: 'accountSellerFundsFlow',
|
||||||
|
component: () => import('../views/account/sellerFundsFlow.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '资金流水',
|
||||||
|
description: '卖家收款/提现流水记录',
|
||||||
|
allAuthority: ['all']
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'orders',
|
path: 'orders',
|
||||||
name: 'accountOrders',
|
name: 'accountOrders',
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ service.interceptors.request.use(config => {
|
|||||||
// 是否需要设置 token
|
// 是否需要设置 token
|
||||||
let token
|
let token
|
||||||
try {
|
try {
|
||||||
token = JSON.parse(localStorage.getItem('token'))
|
token = JSON.parse(localStorage.getItem('leasToken'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ service.interceptors.response.use(res => {
|
|||||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
if (code === 421) {
|
if (code === 421) {
|
||||||
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
|
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('leasToken')
|
||||||
// 系统状态已过期,请重新点击SUPPORT按钮进入
|
// 系统状态已过期,请重新点击SUPPORT按钮进入
|
||||||
superReportError = localStorage.getItem('superReportError')
|
superReportError = localStorage.getItem('superReportError')
|
||||||
if (!superReportError) {
|
if (!superReportError) {
|
||||||
@@ -295,7 +295,7 @@ service.interceptors.response.use(res => {
|
|||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
window.vm.$router.push(`/${window.vm.$i18n.locale}/`)
|
window.vm.$router.push(`/${window.vm.$i18n.locale}/`)
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('leasToken')
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
<!-- 左侧导航 -->
|
<!-- 左侧导航 -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<nav class="side-nav">
|
<nav class="side-nav">
|
||||||
<div class="user-info-card" role="region" v-show="userEmail" aria-label="用户信息" tabindex="0">
|
<!-- <div class="user-info-card" role="region" v-show="userEmail" aria-label="用户信息" tabindex="0">
|
||||||
<div class="user-meta">
|
<div class="user-meta">
|
||||||
<div class="user-email" :title="userEmail || '未登录'">{{ userEmail || '未登录' }}</div>
|
<div class="user-email" :title="userEmail || '未登录'">{{ userEmail || '未登录' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="user-role" role="group" aria-label="导航分组切换">
|
<div class="user-role" role="group" aria-label="导航分组切换">
|
||||||
<button
|
<button
|
||||||
class="role-button"
|
class="role-button"
|
||||||
|
|||||||
@@ -938,27 +938,17 @@ export default {
|
|||||||
*/
|
*/
|
||||||
handleDownloadClient(types) {
|
handleDownloadClient(types) {
|
||||||
// 走后端接口下载客户端程序
|
// 走后端接口下载客户端程序
|
||||||
|
let userEmail = "";
|
||||||
let userEmail = JSON.parse(localStorage.getItem("leasEmail"));
|
try {
|
||||||
if (!userEmail) {
|
const email = localStorage.getItem("leasEmail");
|
||||||
// 弹出确认框,用户确认后跳转到外部网站
|
if (email) {
|
||||||
this.$confirm("获取用户信息失败,请重新进入网站?", "提示", {
|
userEmail = JSON.parse(email);
|
||||||
confirmButtonText: "确认",
|
}
|
||||||
cancelButtonText: "取消",
|
} catch (e) {
|
||||||
type: "warning",
|
// 忽略解析错误,userEmail 保持为空字符串
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// 用户点击确认,跳转到外部网站
|
|
||||||
window.open("https://test.m2pool.com/", "_blank");
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// 用户点击取消,不执行任何操作
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
console.log(userEmail, "userEmail");
|
|
||||||
|
|
||||||
this.downloadUrl = ` ${request.defaults.baseURL}/lease/user/downloadClient?userEmail=${userEmail}&type=${types}`;
|
this.downloadUrl = `${request.defaults.baseURL}/lease/user/downloadClient?userEmail=${userEmail || ""}&type=${types}`;
|
||||||
let a = document.createElement(`a`);
|
let a = document.createElement(`a`);
|
||||||
a.href = this.downloadUrl;
|
a.href = this.downloadUrl;
|
||||||
a.click();
|
a.click();
|
||||||
|
|||||||
@@ -116,3 +116,4 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="loginForm.password"
|
v-model="loginForm.password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="请输入密码"
|
placeholder="请输入密码(8-32位)"
|
||||||
prefix-icon="el-icon-lock"
|
prefix-icon="el-icon-lock"
|
||||||
size="large"
|
size="large"
|
||||||
show-password
|
show-password
|
||||||
@@ -55,6 +55,11 @@
|
|||||||
@keyup.enter.native="handleLogin"
|
@keyup.enter.native="handleLogin"
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<!-- 密码规则提示 -->
|
||||||
|
<div class="password-tip">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
<span>密码需包含大小写字母、数字和特殊字符,长度8-32位</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 验证码输入框 -->
|
<!-- 验证码输入框 -->
|
||||||
@@ -65,7 +70,7 @@
|
|||||||
placeholder="请输入邮箱验证码"
|
placeholder="请输入邮箱验证码"
|
||||||
prefix-icon="el-icon-key"
|
prefix-icon="el-icon-key"
|
||||||
size="large"
|
size="large"
|
||||||
maxlength="6"
|
maxlength="10"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter.native="handleLogin"
|
@keyup.enter.native="handleLogin"
|
||||||
>
|
>
|
||||||
@@ -78,14 +83,14 @@
|
|||||||
:loading="sendingCode"
|
:loading="sendingCode"
|
||||||
@click="handleSendCode"
|
@click="handleSendCode"
|
||||||
>
|
>
|
||||||
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
|
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 记住密码和忘记密码 -->
|
<!-- 忘记密码 -->
|
||||||
<div class="auth-options">
|
<div class="auth-options">
|
||||||
<el-checkbox v-model="rememberMe">记住密码</el-checkbox>
|
<span></span>
|
||||||
<router-link to="/reset-password" class="link-text">
|
<router-link to="/reset-password" class="link-text">
|
||||||
忘记密码?
|
忘记密码?
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -118,6 +123,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getLogin, sendLoginCode } from '@/api/user'
|
import { getLogin, sendLoginCode } from '@/api/user'
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LoginPage',
|
name: 'LoginPage',
|
||||||
@@ -131,7 +137,8 @@
|
|||||||
callback(new Error('请输入邮箱'))
|
callback(new Error('请输入邮箱'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
// 邮箱验证正则:标准邮箱格式
|
||||||
|
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
if (!emailReg.test(value)) {
|
if (!emailReg.test(value)) {
|
||||||
callback(new Error('请输入有效的邮箱地址'))
|
callback(new Error('请输入有效的邮箱地址'))
|
||||||
return
|
return
|
||||||
@@ -142,15 +149,24 @@
|
|||||||
/**
|
/**
|
||||||
* 密码格式验证
|
* 密码格式验证
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* 密码格式验证
|
||||||
|
* 8-32位,包含大小写字母、数字和特殊字符
|
||||||
|
*/
|
||||||
const validatePassword = (rule, value, callback) => {
|
const validatePassword = (rule, value, callback) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
callback(new Error('请输入密码'))
|
callback(new Error('请输入密码'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (value.length < 6) {
|
|
||||||
callback(new Error('密码长度不能少于6位'))
|
// 密码验证正则:8-32位,包含大小写字母、数字和特殊字符(!@#¥%……&.*)
|
||||||
|
const regexPassword = /^(?!.*[\u4e00-\u9fa5])(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,32}$/
|
||||||
|
|
||||||
|
if (!regexPassword.test(value)) {
|
||||||
|
callback(new Error('密码应包含大小写字母、数字和特殊字符,长度8-32位'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,13 +188,10 @@
|
|||||||
],
|
],
|
||||||
code: [
|
code: [
|
||||||
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
|
{ min: 1, max: 10, message: '验证码长度为1-10位', trigger: 'blur' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
// 记住密码
|
|
||||||
rememberMe: false,
|
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
loading: false,
|
loading: false,
|
||||||
sendingCode: false,
|
sendingCode: false,
|
||||||
@@ -190,13 +203,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// 从localStorage读取记住的密码
|
// 从URL参数获取邮箱(如果有)
|
||||||
const savedEmail = localStorage.getItem('rememberedEmail')
|
if (this.$route.query.email) {
|
||||||
const savedPassword = localStorage.getItem('rememberedPassword')
|
this.loginForm.email = this.$route.query.email
|
||||||
if (savedEmail && savedPassword) {
|
|
||||||
this.loginForm.email = savedEmail
|
|
||||||
this.loginForm.password = savedPassword
|
|
||||||
this.rememberMe = true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -227,13 +236,25 @@
|
|||||||
* 发送邮箱验证码
|
* 发送邮箱验证码
|
||||||
*/
|
*/
|
||||||
async handleSendCode() {
|
async handleSendCode() {
|
||||||
// 先验证邮箱
|
// 先检查邮箱是否有值
|
||||||
try {
|
const email = (this.loginForm.email || '').trim()
|
||||||
await this.$refs.loginForm.validateField('email')
|
if (!email) {
|
||||||
} catch (error) {
|
this.$message.warning('请输入邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.loginForm.validateField('email')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动验证邮箱格式
|
||||||
|
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
|
if (!emailReg.test(email)) {
|
||||||
|
this.$message.warning('请输入有效的邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.loginForm.validateField('email')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式验证通过,才发送请求
|
||||||
this.sendingCode = true
|
this.sendingCode = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -291,32 +312,47 @@
|
|||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 登录接口参数:email(邮箱)、password(密码)、code(验证码)
|
// 密码RSA加密(优先使用同步方法,失败则使用异步方法)
|
||||||
|
const passwordPlain = this.loginForm.password
|
||||||
|
let encryptedPassword = passwordPlain
|
||||||
|
|
||||||
|
// 尝试同步加密
|
||||||
|
const syncEncrypted = rsaEncryptSync(passwordPlain)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedPassword = syncEncrypted
|
||||||
|
} else {
|
||||||
|
// 同步加密失败,尝试异步加密
|
||||||
|
const asyncEncrypted = await rsaEncrypt(passwordPlain)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedPassword = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('密码加密失败,请重试')
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录接口参数:email(邮箱)、password(RSA加密后的密码)、code(验证码)
|
||||||
const res = await getLogin({
|
const res = await getLogin({
|
||||||
email: this.loginForm.email,
|
email: this.loginForm.email,
|
||||||
password: this.loginForm.password,
|
password: encryptedPassword,
|
||||||
code: this.loginForm.code
|
code: this.loginForm.code
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
// 保存token到localStorage
|
// 保存access_token到localStorage
|
||||||
const token = res.data.token
|
const accessToken = res.data.access_token
|
||||||
localStorage.setItem('token', JSON.stringify(token))
|
if (accessToken) {
|
||||||
|
localStorage.setItem('leasToken', JSON.stringify(accessToken))
|
||||||
// 保存用户信息
|
|
||||||
if (res.data.userInfo) {
|
|
||||||
localStorage.setItem('userInfo', JSON.stringify(res.data.userInfo))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记住密码
|
// 保存用户信息(包含userName和expires_in)
|
||||||
if (this.rememberMe) {
|
const userInfo = {
|
||||||
localStorage.setItem('rememberedEmail', this.loginForm.email)
|
userName: res.data.userName || this.loginForm.email,
|
||||||
localStorage.setItem('rememberedPassword', this.loginForm.password)
|
expires_in: res.data.expires_in || null
|
||||||
} else {
|
|
||||||
localStorage.removeItem('rememberedEmail')
|
|
||||||
localStorage.removeItem('rememberedPassword')
|
|
||||||
}
|
}
|
||||||
|
localStorage.setItem('userInfo', JSON.stringify(userInfo))
|
||||||
|
localStorage.setItem('leasEmail', this.loginForm.email)
|
||||||
this.$message.success('登录成功')
|
this.$message.success('登录成功')
|
||||||
|
|
||||||
// 跳转到首页或者来源页面
|
// 跳转到首页或者来源页面
|
||||||
@@ -493,6 +529,33 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 密码规则提示 */
|
||||||
|
.password-tip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f5f7ff;
|
||||||
|
border-left: 3px solid #667eea;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip .el-icon-info {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 选项行样式 */
|
/* 选项行样式 */
|
||||||
.auth-options {
|
.auth-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
placeholder="请输入邮箱验证码"
|
placeholder="请输入邮箱验证码"
|
||||||
prefix-icon="el-icon-key"
|
prefix-icon="el-icon-key"
|
||||||
size="large"
|
size="large"
|
||||||
maxlength="6"
|
maxlength="10"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
:loading="sendingCode"
|
:loading="sendingCode"
|
||||||
@click="handleSendCode"
|
@click="handleSendCode"
|
||||||
>
|
>
|
||||||
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
|
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -70,13 +70,18 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="registerForm.password"
|
v-model="registerForm.password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="请输入密码(至少6位)"
|
placeholder="请输入密码(8-32位)"
|
||||||
prefix-icon="el-icon-lock"
|
prefix-icon="el-icon-lock"
|
||||||
size="large"
|
size="large"
|
||||||
show-password
|
show-password
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<!-- 密码规则提示 -->
|
||||||
|
<div class="password-tip">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
<span>密码需包含大小写字母、数字和特殊字符,长度8-32位</span>
|
||||||
|
</div>
|
||||||
<!-- 密码强度提示 -->
|
<!-- 密码强度提示 -->
|
||||||
<div v-if="registerForm.password" class="password-strength">
|
<div v-if="registerForm.password" class="password-strength">
|
||||||
<span class="strength-label">密码强度:</span>
|
<span class="strength-label">密码强度:</span>
|
||||||
@@ -162,20 +167,22 @@ export default {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码格式验证
|
* 密码格式验证
|
||||||
|
* 8-32位,包含大小写字母、数字和特殊字符
|
||||||
*/
|
*/
|
||||||
const validatePassword = (rule, value, callback) => {
|
const validatePassword = (rule, value, callback) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
callback(new Error('请输入密码'))
|
callback(new Error('请输入密码'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (value.length < 6) {
|
|
||||||
callback(new Error('密码长度不能少于6位'))
|
// 密码验证正则:8-32位,包含大小写字母、数字和特殊字符(!@#¥%……&.*)
|
||||||
return
|
const regexPassword = /^(?!.*[\u4e00-\u9fa5])(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,32}$/
|
||||||
}
|
|
||||||
if (value.length > 20) {
|
if (!regexPassword.test(value)) {
|
||||||
callback(new Error('密码长度不能超过20位'))
|
callback(new Error('密码应包含大小写字母、数字和特殊字符,长度8-32位'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +229,7 @@ export default {
|
|||||||
],
|
],
|
||||||
code: [
|
code: [
|
||||||
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
|
{ min: 1, max: 10, message: '验证码长度为1-10位', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
password: [
|
password: [
|
||||||
{ required: true, validator: validatePassword, trigger: 'blur' }
|
{ required: true, validator: validatePassword, trigger: 'blur' }
|
||||||
@@ -322,13 +329,25 @@ export default {
|
|||||||
* 发送邮箱验证码
|
* 发送邮箱验证码
|
||||||
*/
|
*/
|
||||||
async handleSendCode() {
|
async handleSendCode() {
|
||||||
// 先验证邮箱
|
// 先检查邮箱是否有值
|
||||||
try {
|
const email = (this.registerForm.email || '').trim()
|
||||||
await this.$refs.registerForm.validateField('email')
|
if (!email) {
|
||||||
} catch (error) {
|
this.$message.warning('请输入邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.registerForm.validateField('email')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动验证邮箱格式
|
||||||
|
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
|
if (!emailReg.test(email)) {
|
||||||
|
this.$message.warning('请输入有效的邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.registerForm.validateField('email')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式验证通过,才发送请求
|
||||||
this.sendingCode = true
|
this.sendingCode = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -597,12 +616,40 @@ export default {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 密码规则提示 */
|
||||||
|
.password-tip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f5f7ff;
|
||||||
|
border-left: 3px solid #667eea;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip .el-icon-info {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 密码强度提示 */
|
/* 密码强度提示 */
|
||||||
.password-strength {
|
.password-strength {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 8px;
|
margin-top: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.strength-label {
|
.strength-label {
|
||||||
@@ -611,9 +658,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.strength-bar {
|
.strength-bar {
|
||||||
padding: 2px 12px;
|
padding: 1px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.strength-bar.weak {
|
.strength-bar.weak {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
placeholder="请输入邮箱验证码"
|
placeholder="请输入邮箱验证码"
|
||||||
prefix-icon="el-icon-key"
|
prefix-icon="el-icon-key"
|
||||||
size="large"
|
size="large"
|
||||||
maxlength="6"
|
maxlength="10"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
:loading="sendingCode"
|
:loading="sendingCode"
|
||||||
@click="handleSendCode"
|
@click="handleSendCode"
|
||||||
>
|
>
|
||||||
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
|
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -70,13 +70,18 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="resetForm.password"
|
v-model="resetForm.password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="请输入新密码(至少6位)"
|
placeholder="请输入新密码(8-32位)"
|
||||||
prefix-icon="el-icon-lock"
|
prefix-icon="el-icon-lock"
|
||||||
size="large"
|
size="large"
|
||||||
show-password
|
show-password
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<!-- 密码规则提示 -->
|
||||||
|
<div class="password-tip">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
<span>密码需包含大小写字母、数字和特殊字符,长度8-32位</span>
|
||||||
|
</div>
|
||||||
<!-- 密码强度提示 -->
|
<!-- 密码强度提示 -->
|
||||||
<div v-if="resetForm.password" class="password-strength">
|
<div v-if="resetForm.password" class="password-strength">
|
||||||
<span class="strength-label">密码强度:</span>
|
<span class="strength-label">密码强度:</span>
|
||||||
@@ -127,6 +132,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { updatePassword, sendUpdatePwdCode } from '@/api/user'
|
import { updatePassword, sendUpdatePwdCode } from '@/api/user'
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ResetPasswordPage',
|
name: 'ResetPasswordPage',
|
||||||
@@ -140,7 +146,8 @@ export default {
|
|||||||
callback(new Error('请输入邮箱'))
|
callback(new Error('请输入邮箱'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
// 邮箱验证正则:标准邮箱格式
|
||||||
|
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
if (!emailReg.test(value)) {
|
if (!emailReg.test(value)) {
|
||||||
callback(new Error('请输入有效的邮箱地址'))
|
callback(new Error('请输入有效的邮箱地址'))
|
||||||
return
|
return
|
||||||
@@ -151,19 +158,24 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 密码格式验证
|
* 密码格式验证
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* 密码格式验证
|
||||||
|
* 8-32位,包含大小写字母、数字和特殊字符
|
||||||
|
*/
|
||||||
const validatePassword = (rule, value, callback) => {
|
const validatePassword = (rule, value, callback) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
callback(new Error('请输入新密码'))
|
callback(new Error('请输入新密码'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (value.length < 6) {
|
|
||||||
callback(new Error('密码长度不能少于6位'))
|
// 密码验证正则:8-32位,包含大小写字母、数字和特殊字符(!@#¥%……&.*)
|
||||||
return
|
const regexPassword = /^(?!.*[\u4e00-\u9fa5])(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,32}$/
|
||||||
}
|
|
||||||
if (value.length > 20) {
|
if (!regexPassword.test(value)) {
|
||||||
callback(new Error('密码长度不能超过20位'))
|
callback(new Error('密码应包含大小写字母、数字和特殊字符,长度8-32位'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +210,7 @@ export default {
|
|||||||
],
|
],
|
||||||
code: [
|
code: [
|
||||||
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
|
{ min: 1, max: 10, message: '验证码长度为1-10位', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
password: [
|
password: [
|
||||||
{ required: true, validator: validatePassword, trigger: 'blur' }
|
{ required: true, validator: validatePassword, trigger: 'blur' }
|
||||||
@@ -295,13 +307,25 @@ export default {
|
|||||||
* 发送邮箱验证码
|
* 发送邮箱验证码
|
||||||
*/
|
*/
|
||||||
async handleSendCode() {
|
async handleSendCode() {
|
||||||
// 先验证邮箱
|
// 先检查邮箱是否有值
|
||||||
try {
|
const email = (this.resetForm.email || '').trim()
|
||||||
await this.$refs.resetForm.validateField('email')
|
if (!email) {
|
||||||
} catch (error) {
|
this.$message.warning('请输入邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.resetForm.validateField('email')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动验证邮箱格式
|
||||||
|
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
|
if (!emailReg.test(email)) {
|
||||||
|
this.$message.warning('请输入有效的邮箱地址')
|
||||||
|
// 触发表单验证,显示错误提示
|
||||||
|
this.$refs.resetForm.validateField('email')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式验证通过,才发送请求
|
||||||
this.sendingCode = true
|
this.sendingCode = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -359,11 +383,31 @@ export default {
|
|||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 修改密码接口参数:email(邮箱)、code(验证码)、password(新密码)
|
// 密码RSA加密(优先使用同步方法,失败则使用异步方法)
|
||||||
|
const passwordPlain = this.resetForm.password
|
||||||
|
let encryptedPassword = passwordPlain
|
||||||
|
|
||||||
|
// 尝试同步加密
|
||||||
|
const syncEncrypted = rsaEncryptSync(passwordPlain)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedPassword = syncEncrypted
|
||||||
|
} else {
|
||||||
|
// 同步加密失败,尝试异步加密
|
||||||
|
const asyncEncrypted = await rsaEncrypt(passwordPlain)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedPassword = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('密码加密失败,请重试')
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码接口参数:email(邮箱)、code(验证码)、password(RSA加密后的新密码)
|
||||||
const res = await updatePassword({
|
const res = await updatePassword({
|
||||||
email: this.resetForm.email,
|
email: this.resetForm.email,
|
||||||
code: this.resetForm.code,
|
code: this.resetForm.code,
|
||||||
password: this.resetForm.password
|
password: encryptedPassword
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
@@ -549,12 +593,40 @@ export default {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 密码规则提示 */
|
||||||
|
.password-tip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f5f7ff;
|
||||||
|
border-left: 3px solid #667eea;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-tip .el-icon-info {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 密码强度提示 */
|
/* 密码强度提示 */
|
||||||
.password-strength {
|
.password-strength {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 8px;
|
margin-top: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.strength-label {
|
.strength-label {
|
||||||
@@ -563,9 +635,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.strength-bar {
|
.strength-bar {
|
||||||
padding: 2px 12px;
|
padding: 1px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.strength-bar.weak {
|
.strength-bar.weak {
|
||||||
|
|||||||
Reference in New Issue
Block a user