每周五更新

This commit is contained in:
2025-12-26 10:58:16 +08:00
parent c4f4babe1d
commit e9bca8163d
10 changed files with 290 additions and 105 deletions

View File

@@ -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')

View File

@@ -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';

View File

@@ -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',

View File

@@ -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')
}); });
} }

View File

@@ -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"

View File

@@ -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();

View File

@@ -116,3 +116,4 @@ export default {
</style> </style>

View File

@@ -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邮箱、passwordRSA加密后的密码、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;

View File

@@ -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 {

View File

@@ -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验证码、passwordRSA加密后的新密码
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 {