每周五更新

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
isLoggedIn() {
const token = localStorage.getItem('token')
const token = JSON.parse(localStorage.getItem('leasToken'))
return !!token // 有token就是已登录没有就是未登录
}
},
@@ -179,7 +179,7 @@ export default {
},
/**
* 加载用户邮箱
* 从localStorage读取用户信息获取邮箱
* 从localStorage读取用户信息获取用户名
*/
loadUserEmail() {
try {
@@ -187,8 +187,8 @@ export default {
const userInfoStr = localStorage.getItem('userInfo')
if (userInfoStr) {
const userInfo = JSON.parse(userInfoStr)
// 获取邮箱,如果没有邮箱就显示用户名
this.userEmail = userInfo.email || userInfo.username || '用户'
// 获取用户名userName字段,如果没有就显示默认值
this.userEmail = userInfo.userName || userInfo.email || userInfo.username || '用户'
}
} catch (e) {
console.error('读取用户信息失败:', e)
@@ -201,7 +201,7 @@ export default {
*/
handleLogout() {
// 清除localStorage中的所有登录信息
localStorage.removeItem('token')
localStorage.removeItem('leasToken')
localStorage.removeItem('userInfo')
localStorage.removeItem('leasEmail')
localStorage.removeItem('userId')

View File

@@ -5,7 +5,7 @@ import store from './store'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入登录信息处理
import './utils/loginInfo.js';
// import './utils/loginInfo.js';
// 全局输入防表情守卫(极简、无侵入)
import { initNoEmojiGuard } from './utils/noEmojiGuard.js';

View File

@@ -223,6 +223,16 @@ export const accountRoutes = [
allAuthority: ['all']
}
},
{
path: 'seller-funds-flow',
name: 'accountSellerFundsFlow',
component: () => import('../views/account/sellerFundsFlow.vue'),
meta: {
title: '资金流水',
description: '卖家收款/提现流水记录',
allAuthority: ['all']
}
},
{
path: 'orders',
name: 'accountOrders',

View File

@@ -192,7 +192,7 @@ service.interceptors.request.use(config => {
// 是否需要设置 token
let token
try {
token = JSON.parse(localStorage.getItem('token'))
token = JSON.parse(localStorage.getItem('leasToken'))
} catch (e) {
console.log(e);
}
@@ -275,7 +275,7 @@ service.interceptors.response.use(res => {
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (code === 421) {
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
localStorage.removeItem('token')
localStorage.removeItem('leasToken')
// 系统状态已过期请重新点击SUPPORT按钮进入
superReportError = localStorage.getItem('superReportError')
if (!superReportError) {
@@ -295,7 +295,7 @@ service.interceptors.response.use(res => {
localStorage.removeItem('token')
}).catch(() => {
window.vm.$router.push(`/${window.vm.$i18n.locale}/`)
localStorage.removeItem('token')
localStorage.removeItem('leasToken')
});
}

View File

@@ -8,11 +8,11 @@
<!-- 左侧导航 -->
<aside class="sidebar">
<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-email" :title="userEmail || '未登录'">{{ userEmail || '未登录' }}</div>
</div>
</div>
</div> -->
<div class="user-role" role="group" aria-label="导航分组切换">
<button
class="role-button"

View File

@@ -938,27 +938,17 @@ export default {
*/
handleDownloadClient(types) {
// 走后端接口下载客户端程序
let userEmail = JSON.parse(localStorage.getItem("leasEmail"));
if (!userEmail) {
// 弹出确认框,用户确认后跳转到外部网站
this.$confirm("获取用户信息失败,请重新进入网站?", "提示", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
// 用户点击确认,跳转到外部网站
window.open("https://test.m2pool.com/", "_blank");
})
.catch(() => {
// 用户点击取消,不执行任何操作
});
return;
let userEmail = "";
try {
const email = localStorage.getItem("leasEmail");
if (email) {
userEmail = JSON.parse(email);
}
} catch (e) {
// 忽略解析错误userEmail 保持为空字符串
}
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`);
a.href = this.downloadUrl;
a.click();

View File

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

View File

@@ -47,7 +47,7 @@
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
placeholder="请输入密码8-32位"
prefix-icon="el-icon-lock"
size="large"
show-password
@@ -55,6 +55,11 @@
@keyup.enter.native="handleLogin"
>
</el-input>
<!-- 密码规则提示 -->
<div class="password-tip">
<i class="el-icon-info"></i>
<span>密码需包含大小写字母数字和特殊字符长度8-32</span>
</div>
</el-form-item>
<!-- 验证码输入框 -->
@@ -65,7 +70,7 @@
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
maxlength="10"
clearable
@keyup.enter.native="handleLogin"
>
@@ -78,14 +83,14 @@
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 记住密码和忘记密码 -->
<!-- 忘记密码 -->
<div class="auth-options">
<el-checkbox v-model="rememberMe">记住密码</el-checkbox>
<span></span>
<router-link to="/reset-password" class="link-text">
忘记密码
</router-link>
@@ -118,6 +123,7 @@
<script>
import { getLogin, sendLoginCode } from '@/api/user'
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
export default {
name: 'LoginPage',
@@ -131,7 +137,8 @@
callback(new Error('请输入邮箱'))
return
}
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
// 邮箱验证正则:标准邮箱格式
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
if (!emailReg.test(value)) {
callback(new Error('请输入有效的邮箱地址'))
return
@@ -142,15 +149,24 @@
/**
* 密码格式验证
*/
/**
* 密码格式验证
* 8-32位包含大小写字母、数字和特殊字符
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
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
}
callback()
}
@@ -172,13 +188,10 @@
],
code: [
{ 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,
sendingCode: false,
@@ -190,13 +203,9 @@
},
mounted() {
// 从localStorage读取记住的密码
const savedEmail = localStorage.getItem('rememberedEmail')
const savedPassword = localStorage.getItem('rememberedPassword')
if (savedEmail && savedPassword) {
this.loginForm.email = savedEmail
this.loginForm.password = savedPassword
this.rememberMe = true
// 从URL参数获取邮箱如果有
if (this.$route.query.email) {
this.loginForm.email = this.$route.query.email
}
},
@@ -227,13 +236,25 @@
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.loginForm.validateField('email')
} catch (error) {
// 先检查邮箱是否有值
const email = (this.loginForm.email || '').trim()
if (!email) {
this.$message.warning('请输入邮箱地址')
// 触发表单验证,显示错误提示
this.$refs.loginForm.validateField('email')
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
try {
@@ -291,32 +312,47 @@
this.loading = true
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({
email: this.loginForm.email,
password: this.loginForm.password,
password: encryptedPassword,
code: this.loginForm.code
})
if (res && res.code === 200) {
// 保存token到localStorage
const token = res.data.token
localStorage.setItem('token', JSON.stringify(token))
// 保存用户信息
if (res.data.userInfo) {
localStorage.setItem('userInfo', JSON.stringify(res.data.userInfo))
// 保存access_token到localStorage
const accessToken = res.data.access_token
if (accessToken) {
localStorage.setItem('leasToken', JSON.stringify(accessToken))
}
// 记住密码
if (this.rememberMe) {
localStorage.setItem('rememberedEmail', this.loginForm.email)
localStorage.setItem('rememberedPassword', this.loginForm.password)
} else {
localStorage.removeItem('rememberedEmail')
localStorage.removeItem('rememberedPassword')
// 保存用户信息包含userName和expires_in
const userInfo = {
userName: res.data.userName || this.loginForm.email,
expires_in: res.data.expires_in || null
}
localStorage.setItem('userInfo', JSON.stringify(userInfo))
localStorage.setItem('leasEmail', this.loginForm.email)
this.$message.success('登录成功')
// 跳转到首页或者来源页面
@@ -493,6 +529,33 @@
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 {
display: flex;

View File

@@ -48,7 +48,7 @@
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
maxlength="10"
clearable
>
</el-input>
@@ -60,7 +60,7 @@
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
@@ -70,13 +70,18 @@
<el-input
v-model="registerForm.password"
type="password"
placeholder="请输入密码(至少6位)"
placeholder="请输入密码(8-32位)"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
>
</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">
<span class="strength-label">密码强度</span>
@@ -162,20 +167,22 @@ export default {
/**
* 密码格式验证
* 8-32位包含大小写字母、数字和特殊字符
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
return
}
if (value.length < 6) {
callback(new Error('密码长度不能少于6位'))
return
}
if (value.length > 20) {
callback(new Error('密码长度不能超过20位'))
// 密码验证正则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
}
callback()
}
@@ -222,7 +229,7 @@ export default {
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
{ min: 1, max: 10, message: '验证码长度为1-10位', trigger: 'blur' }
],
password: [
{ required: true, validator: validatePassword, trigger: 'blur' }
@@ -322,13 +329,25 @@ export default {
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.registerForm.validateField('email')
} catch (error) {
// 先检查邮箱是否有值
const email = (this.registerForm.email || '').trim()
if (!email) {
this.$message.warning('请输入邮箱地址')
// 触发表单验证,显示错误提示
this.$refs.registerForm.validateField('email')
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
try {
@@ -597,12 +616,40 @@ export default {
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 {
display: flex;
align-items: center;
margin-top: 8px;
margin-top: 4px;
font-size: 12px;
line-height: 1.2;
}
.strength-label {
@@ -611,9 +658,10 @@ export default {
}
.strength-bar {
padding: 2px 12px;
padding: 1px 10px;
border-radius: 4px;
font-weight: 600;
line-height: 1.4;
}
.strength-bar.weak {

View File

@@ -48,7 +48,7 @@
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
maxlength="10"
clearable
>
</el-input>
@@ -60,7 +60,7 @@
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
@@ -70,13 +70,18 @@
<el-input
v-model="resetForm.password"
type="password"
placeholder="请输入新密码(至少6位)"
placeholder="请输入新密码(8-32位)"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
>
</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">
<span class="strength-label">密码强度</span>
@@ -127,6 +132,7 @@
<script>
import { updatePassword, sendUpdatePwdCode } from '@/api/user'
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
export default {
name: 'ResetPasswordPage',
@@ -140,7 +146,8 @@ export default {
callback(new Error('请输入邮箱'))
return
}
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
// 邮箱验证正则:标准邮箱格式
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
if (!emailReg.test(value)) {
callback(new Error('请输入有效的邮箱地址'))
return
@@ -151,19 +158,24 @@ export default {
/**
* 密码格式验证
*/
/**
* 密码格式验证
* 8-32位包含大小写字母、数字和特殊字符
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入新密码'))
return
}
if (value.length < 6) {
callback(new Error('密码长度不能少于6位'))
return
}
if (value.length > 20) {
callback(new Error('密码长度不能超过20位'))
// 密码验证正则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
}
callback()
}
@@ -198,7 +210,7 @@ export default {
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
{ min: 1, max: 10, message: '验证码长度为1-10位', trigger: 'blur' }
],
password: [
{ required: true, validator: validatePassword, trigger: 'blur' }
@@ -295,13 +307,25 @@ export default {
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.resetForm.validateField('email')
} catch (error) {
// 先检查邮箱是否有值
const email = (this.resetForm.email || '').trim()
if (!email) {
this.$message.warning('请输入邮箱地址')
// 触发表单验证,显示错误提示
this.$refs.resetForm.validateField('email')
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
try {
@@ -359,11 +383,31 @@ export default {
this.loading = true
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({
email: this.resetForm.email,
code: this.resetForm.code,
password: this.resetForm.password
password: encryptedPassword
})
if (res && res.code === 200) {
@@ -549,12 +593,40 @@ export default {
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 {
display: flex;
align-items: center;
margin-top: 8px;
margin-top: 4px;
font-size: 12px;
line-height: 1.2;
}
.strength-label {
@@ -563,9 +635,10 @@ export default {
}
.strength-bar {
padding: 2px 12px;
padding: 1px 10px;
border-radius: 4px;
font-weight: 600;
line-height: 1.4;
}
.strength-bar.weak {