添加注销账户功能已通过测试
This commit is contained in:
Binary file not shown.
228
mining-pool/src/components/PasswordStrengthIndicator.vue
Normal file
228
mining-pool/src/components/PasswordStrengthIndicator.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="password-strength-indicator">
|
||||
<!-- 密码强度进度条 -->
|
||||
<div class="strength-bar-container">
|
||||
<div
|
||||
class="strength-bar"
|
||||
:class="strengthClass"
|
||||
:style="{ width: strengthPercentage + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 强度文本提示 -->
|
||||
<div class="strength-text" :class="strengthClass">
|
||||
<span v-if="password && password.length > 0">
|
||||
{{ $t(`passwordStrength.${strengthLevel}`) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 详细的校验项列表 -->
|
||||
<div v-if="showDetails && password && password.length > 0" class="validation-checklist">
|
||||
<div
|
||||
v-for="(rule, key) in validationDetails"
|
||||
:key="key"
|
||||
class="validation-item"
|
||||
:class="{ 'valid': rule.passed, 'invalid': !rule.passed }"
|
||||
>
|
||||
<i :class="rule.passed ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
|
||||
<span>{{ $t(rule.message) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { checkPasswordStrength } from '@/utils/passwordValidator'
|
||||
|
||||
export default {
|
||||
name: 'PasswordStrengthIndicator',
|
||||
props: {
|
||||
// 要校验的密码
|
||||
password: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否显示详细的校验项
|
||||
showDetails: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 密码强度分析结果
|
||||
strengthAnalysis() {
|
||||
if (!this.password) {
|
||||
return {
|
||||
score: 0,
|
||||
level: 'none',
|
||||
validation: {
|
||||
valid: false,
|
||||
errors: [],
|
||||
details: {
|
||||
length: false,
|
||||
hasLowercase: false,
|
||||
hasUppercase: false,
|
||||
hasNumber: false,
|
||||
hasSpecialChar: false,
|
||||
noChinese: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return checkPasswordStrength(this.password)
|
||||
},
|
||||
|
||||
// 强度等级
|
||||
strengthLevel() {
|
||||
return this.strengthAnalysis.level
|
||||
},
|
||||
|
||||
// 强度百分比
|
||||
strengthPercentage() {
|
||||
return this.strengthAnalysis.score
|
||||
},
|
||||
|
||||
// 强度等级对应的样式类
|
||||
strengthClass() {
|
||||
return `strength-${this.strengthLevel}`
|
||||
},
|
||||
|
||||
// 校验详情(用于显示✓和✗)
|
||||
validationDetails() {
|
||||
const details = this.strengthAnalysis.validation.details
|
||||
return {
|
||||
length: {
|
||||
passed: details.length,
|
||||
message: 'passwordRules.length'
|
||||
},
|
||||
noChinese: {
|
||||
passed: details.noChinese,
|
||||
message: 'passwordRules.noChinese'
|
||||
},
|
||||
lowercase: {
|
||||
passed: details.hasLowercase,
|
||||
message: 'passwordRules.lowercase'
|
||||
},
|
||||
uppercase: {
|
||||
passed: details.hasUppercase,
|
||||
message: 'passwordRules.uppercase'
|
||||
},
|
||||
number: {
|
||||
passed: details.hasNumber,
|
||||
message: 'passwordRules.number'
|
||||
},
|
||||
specialChar: {
|
||||
passed: details.hasSpecialChar,
|
||||
message: 'passwordRules.specialChar'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 密码变化时通知父组件校验结果
|
||||
strengthAnalysis: {
|
||||
handler(newVal) {
|
||||
this.$emit('strength-change', newVal)
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.password-strength-indicator {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
// 强度进度条容器
|
||||
.strength-bar-container {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-color: #e4e7ed;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
// 强度进度条
|
||||
.strength-bar {
|
||||
height: 100%;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 3px;
|
||||
|
||||
&.strength-weak {
|
||||
background-color: #f56c6c; // 红色 - 弱
|
||||
}
|
||||
|
||||
&.strength-medium {
|
||||
background-color: #e6a23c; // 橙色 - 中等
|
||||
}
|
||||
|
||||
&.strength-strong {
|
||||
background-color: #67c23a; // 绿色 - 强
|
||||
}
|
||||
|
||||
&.strength-none {
|
||||
background-color: #e4e7ed; // 灰色 - 无
|
||||
}
|
||||
}
|
||||
|
||||
// 强度文本
|
||||
.strength-text {
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
|
||||
&.strength-weak {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.strength-medium {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
&.strength-strong {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
&.strength-none {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
// 校验项列表
|
||||
.validation-checklist {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.validation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
i {
|
||||
margin-right: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.valid {
|
||||
color: #67c23a;
|
||||
|
||||
i {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
color: #dcdfe6;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -42,6 +42,32 @@ export const userLang_zh = {
|
||||
modifiedSuccessfully:"密码修改成功,请登录",
|
||||
verificationEnabled:"已开启验证",
|
||||
newPassword2:"新密码",
|
||||
},
|
||||
// 密码错误详细提示
|
||||
passwordError: {
|
||||
tooShort: "密码长度不足8位",
|
||||
tooLong: "密码长度超过32位",
|
||||
containsChinese: "密码不能包含中文字符",
|
||||
noLowercase: "密码必须包含小写字母(a-z)",
|
||||
noUppercase: "密码必须包含大写字母(A-Z)",
|
||||
noNumber: "密码必须包含数字(0-9)",
|
||||
noSpecialChar: "密码必须包含特殊字符,可用字符: ! @ # $ % ^ & * ( ) _ + - = [ ] { } | ; : ' \" , . < > ? / \\ ` ~",
|
||||
},
|
||||
// 密码强度等级
|
||||
passwordStrength: {
|
||||
none: "请输入密码",
|
||||
weak: "密码强度:弱",
|
||||
medium: "密码强度:中等",
|
||||
strong: "密码强度:强"
|
||||
},
|
||||
// 密码规则提示(用于✓✗列表)
|
||||
passwordRules: {
|
||||
length: "长度为8-32位",
|
||||
noChinese: "不包含中文字符",
|
||||
lowercase: "包含小写字母",
|
||||
uppercase: "包含大写字母",
|
||||
number: "包含数字",
|
||||
specialChar: "包含特殊字符"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,5 +115,31 @@ export const userLang_en = {
|
||||
modifiedSuccessfully:"Password changed successfully, please log in",
|
||||
verificationEnabled:"Verification enabled",
|
||||
newPassword2:"New Password",
|
||||
},
|
||||
// Detailed password error messages
|
||||
passwordError: {
|
||||
tooShort: "Password must be at least 8 characters",
|
||||
tooLong: "Password cannot exceed 32 characters",
|
||||
containsChinese: "Password cannot contain Chinese characters",
|
||||
noLowercase: "Password must contain lowercase letters (a-z)",
|
||||
noUppercase: "Password must contain uppercase letters (A-Z)",
|
||||
noNumber: "Password must contain numbers (0-9)",
|
||||
noSpecialChar: "Password must contain special characters. Allowed: ! @ # $ % ^ & * ( ) _ + - = [ ] { } | ; : ' \" , . < > ? / \\ ` ~",
|
||||
},
|
||||
// Password strength levels
|
||||
passwordStrength: {
|
||||
none: "Please enter password",
|
||||
weak: "Password Strength: Weak",
|
||||
medium: "Password Strength: Medium",
|
||||
strong: "Password Strength: Strong"
|
||||
},
|
||||
// Password rules checklist
|
||||
passwordRules: {
|
||||
length: "8-32 characters",
|
||||
noChinese: "No Chinese characters",
|
||||
lowercase: "Contains lowercase letter",
|
||||
uppercase: "Contains uppercase letter",
|
||||
number: "Contains number",
|
||||
specialChar: "Contains special character"
|
||||
}
|
||||
}
|
||||
156
mining-pool/src/utils/passwordValidator.js
Normal file
156
mining-pool/src/utils/passwordValidator.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 密码强度逐项校验工具
|
||||
* @description 提供分步密码校验,精确指出每一项不符合的要求
|
||||
*/
|
||||
|
||||
/**
|
||||
* 密码校验规则配置
|
||||
*/
|
||||
export const PASSWORD_RULES = {
|
||||
minLength: 8,
|
||||
maxLength: 32,
|
||||
requireLowercase: true,
|
||||
requireUppercase: true,
|
||||
requireNumber: true,
|
||||
requireSpecialChar: true,
|
||||
allowChinese: false
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许的特殊字符列表(用于显示给用户)
|
||||
* 实际上所有非字母数字字符都可以,这里列出常用的
|
||||
*/
|
||||
export const ALLOWED_SPECIAL_CHARS = '!@#$%^&*()_+-=[]{}|;:\'",.<>?/\\`~'
|
||||
|
||||
/**
|
||||
* 校验结果接口
|
||||
* @typedef {Object} ValidationResult
|
||||
* @property {boolean} valid - 是否通过校验
|
||||
* @property {Array<string>} errors - 错误信息数组(i18n key)
|
||||
* @property {Object} details - 每项规则的详细校验结果
|
||||
*/
|
||||
|
||||
/**
|
||||
* 密码逐项校验函数
|
||||
* @param {string} password - 待校验的密码
|
||||
* @returns {ValidationResult} 校验结果对象
|
||||
*/
|
||||
export function validatePassword(password) {
|
||||
const result = {
|
||||
valid: true,
|
||||
errors: [],
|
||||
details: {
|
||||
length: false,
|
||||
hasLowercase: false,
|
||||
hasUppercase: false,
|
||||
hasNumber: false,
|
||||
hasSpecialChar: false,
|
||||
noChinese: false
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 检查长度
|
||||
if (password.length < PASSWORD_RULES.minLength) {
|
||||
result.errors.push('passwordError.tooShort')
|
||||
result.valid = false
|
||||
} else if (password.length > PASSWORD_RULES.maxLength) {
|
||||
result.errors.push('passwordError.tooLong')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.length = true
|
||||
}
|
||||
|
||||
// 2. 检查是否包含中文
|
||||
const chineseRegex = /[\u4e00-\u9fa5]/
|
||||
if (chineseRegex.test(password)) {
|
||||
result.errors.push('passwordError.containsChinese')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.noChinese = true
|
||||
}
|
||||
|
||||
// 3. 检查小写字母
|
||||
const lowercaseRegex = /[a-z]/
|
||||
if (!lowercaseRegex.test(password)) {
|
||||
result.errors.push('passwordError.noLowercase')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.hasLowercase = true
|
||||
}
|
||||
|
||||
// 4. 检查大写字母
|
||||
const uppercaseRegex = /[A-Z]/
|
||||
if (!uppercaseRegex.test(password)) {
|
||||
result.errors.push('passwordError.noUppercase')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.hasUppercase = true
|
||||
}
|
||||
|
||||
// 5. 检查数字
|
||||
const numberRegex = /[0-9]/
|
||||
if (!numberRegex.test(password)) {
|
||||
result.errors.push('passwordError.noNumber')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.hasNumber = true
|
||||
}
|
||||
|
||||
// 6. 检查特殊字符
|
||||
const specialCharRegex = /[\W_]/
|
||||
if (!specialCharRegex.test(password)) {
|
||||
result.errors.push('passwordError.noSpecialChar')
|
||||
result.valid = false
|
||||
} else {
|
||||
result.details.hasSpecialChar = true
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时校验密码强度(适用于input输入时)
|
||||
* @param {string} password - 当前输入的密码
|
||||
* @returns {Object} 强度分析结果
|
||||
*/
|
||||
export function checkPasswordStrength(password) {
|
||||
const validation = validatePassword(password)
|
||||
|
||||
// 计算强度分数(0-100)
|
||||
const maxScore = 6
|
||||
const passedRules = Object.values(validation.details).filter(v => v).length
|
||||
const score = Math.round((passedRules / maxScore) * 100)
|
||||
|
||||
// 强度等级
|
||||
let level = 'weak'
|
||||
if (score >= 85) level = 'strong'
|
||||
else if (score >= 50) level = 'medium'
|
||||
|
||||
return {
|
||||
score,
|
||||
level,
|
||||
validation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取友好的错误提示(用于Message组件)
|
||||
* @param {Array<string>} errorKeys - 错误的i18n key数组
|
||||
* @param {Function} t - vue-i18n的$t函数
|
||||
* @returns {string} 格式化的错误提示文本
|
||||
*/
|
||||
export function formatPasswordErrors(errorKeys, t) {
|
||||
if (!errorKeys || errorKeys.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 翻译所有错误信息
|
||||
const errorMessages = errorKeys.map(key => t(key))
|
||||
|
||||
// 格式化为列表形式
|
||||
if (errorMessages.length === 1) {
|
||||
return errorMessages[0]
|
||||
}
|
||||
|
||||
return errorMessages.map((msg, index) => `${index + 1}. ${msg}`).join('\n')
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
<el-input
|
||||
prefix-icon="el-icon-user"
|
||||
v-model="loginForm.userName"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t('user.Account')"
|
||||
></el-input>
|
||||
@@ -38,6 +39,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
@@ -50,6 +52,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="loginForm.code"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -132,6 +135,7 @@
|
||||
<el-input
|
||||
prefix-icon="el-icon-user"
|
||||
v-model="loginForm.userName"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t('user.Account')"
|
||||
type="email"
|
||||
@@ -141,6 +145,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
@@ -153,6 +158,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="loginForm.code"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -213,6 +219,7 @@ import {
|
||||
} from "../../api/login";
|
||||
import { encryption } from "../../utils/fun";
|
||||
import { getAccountList } from "../../api/personalCenter";
|
||||
import { validatePassword, formatPasswordErrors } from "../../utils/passwordValidator";
|
||||
|
||||
|
||||
export default {
|
||||
@@ -709,16 +716,16 @@ export default {
|
||||
// }
|
||||
}
|
||||
|
||||
// 密码验证 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}$/; // 正则表达式
|
||||
const PasswordIsValid = regexPassword.test(this.loginForm.password);
|
||||
if (!PasswordIsValid) {
|
||||
// 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
|
||||
// 密码验证 - 使用优化后的详细校验逻辑
|
||||
const validation = validatePassword(this.loginForm.password);
|
||||
if (!validation.valid) {
|
||||
// 生成友好的逐项错误提示
|
||||
const errorMessage = formatPasswordErrors(validation.errors, this.$t.bind(this));
|
||||
this.$message({
|
||||
message: this.$t(`user.PasswordReminder`),
|
||||
message: errorMessage,
|
||||
type: "error",
|
||||
showClose: true,
|
||||
duration: 5000, // 延长显示时间,因为错误信息可能有多条
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -734,6 +741,38 @@ export default {
|
||||
handleClick() {
|
||||
this.$router.push(`/${this.lang}`);
|
||||
},
|
||||
/**
|
||||
* 去除输入值中的空格
|
||||
* @param {string} value - 输入值
|
||||
* @returns {string} 去除空格后的值
|
||||
*/
|
||||
removeSpaces(value) {
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\s/g, '');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
/**
|
||||
* 处理邮箱输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleEmailInput(value) {
|
||||
this.loginForm.userName = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handlePasswordInput(value) {
|
||||
this.loginForm.password = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理验证码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleCodeInput(value) {
|
||||
this.loginForm.code = this.removeSpaces(value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getIfBind, getBindInfo, getBindGoogle, getBindCode, getCloseStepTwo, getCloseCode, getUpdatePwd, getUpdatePwdCode } from "../../../api/personalCenter"
|
||||
import { encryption } from "../../../utils/fun";
|
||||
import { getResetPwd, getResetPwdCode, sendCloseAccount, closeAccount } from "../../../api/login"
|
||||
import { validatePassword, formatPasswordErrors } from "../../../utils/passwordValidator";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -250,6 +251,8 @@ export default {
|
||||
this.dialogVisible = false
|
||||
this.verificationDialogVisible = false
|
||||
localStorage.removeItem("token")
|
||||
// 修改密码成功后跳转到登录页面
|
||||
const lang = this.$i18n.locale || this.lang || 'zh';
|
||||
this.$router.push(`/${lang}/login`);
|
||||
}
|
||||
this.setLoading('ResetPwdLoading', false);
|
||||
@@ -601,17 +604,16 @@ export default {
|
||||
|
||||
}
|
||||
|
||||
// 密码验证 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}$/; // 正则表达式
|
||||
|
||||
const PasswordIsValid = regexPassword.test(this.changePasswordParams.password);
|
||||
if (!PasswordIsValid) {
|
||||
// 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
|
||||
// 密码验证 - 使用优化后的详细校验逻辑
|
||||
const validation = validatePassword(this.changePasswordParams.password);
|
||||
if (!validation.valid) {
|
||||
// 生成友好的逐项错误提示
|
||||
const errorMessage = formatPasswordErrors(validation.errors, this.$t.bind(this));
|
||||
this.$message({
|
||||
message: this.$t(`user.PasswordReminder`),
|
||||
message: errorMessage,
|
||||
type: "error",
|
||||
showClose: true
|
||||
showClose: true,
|
||||
duration: 5000, // 延长显示时间,因为错误信息可能有多条
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -803,6 +805,49 @@ export default {
|
||||
// this.bthText="user.obtainVerificationCode"
|
||||
// this.time = "";
|
||||
},
|
||||
/**
|
||||
* 去除输入值中的空格
|
||||
* @param {string} value - 输入值
|
||||
* @returns {string} 去除空格后的值
|
||||
*/
|
||||
removeSpaces(value) {
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\s/g, '');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
/**
|
||||
* 处理密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
* @param {string} field - 字段名
|
||||
*/
|
||||
handlePasswordInput(value, field) {
|
||||
const trimmedValue = this.removeSpaces(value);
|
||||
if (field === 'password') {
|
||||
this.changePasswordParams.password = trimmedValue;
|
||||
} else if (field === 'newPassword') {
|
||||
this.newPassword = trimmedValue;
|
||||
} else if (field === 'pwd') {
|
||||
this.params.pwd = trimmedValue;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 处理验证码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
* @param {string} field - 字段名
|
||||
*/
|
||||
handleCodeInput(value, field) {
|
||||
const trimmedValue = this.removeSpaces(value);
|
||||
if (field === 'updatePwdCode') {
|
||||
this.changePasswordParams.updatePwdCode = trimmedValue;
|
||||
} else if (field === 'eCode') {
|
||||
this.closeParams.eCode = trimmedValue;
|
||||
} else if (field === 'paramsECode') {
|
||||
this.params.eCode = trimmedValue;
|
||||
} else if (field === 'deleteAccountECode') {
|
||||
this.deleteAccountParams.eCode = trimmedValue;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
<el-input
|
||||
type="email"
|
||||
v-model="changePasswordParams.updatePwdCode"
|
||||
@input="handleCodeInput($event, 'updatePwdCode')"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -223,6 +224,7 @@
|
||||
type="password"
|
||||
showPassword
|
||||
v-model="changePasswordParams.password"
|
||||
@input="handlePasswordInput($event, 'password')"
|
||||
:placeholder="$t(`personal.pleaseEnter`)"
|
||||
></el-input>
|
||||
<p class="remind" :title="$t(`user.passwordPrompt`)">
|
||||
@@ -236,8 +238,10 @@
|
||||
type="password"
|
||||
showPassword
|
||||
v-model="newPassword"
|
||||
@input="handlePasswordInput($event, 'newPassword')"
|
||||
:placeholder="$t(`personal.pleaseEnter`)"
|
||||
></el-input>
|
||||
|
||||
</div>
|
||||
<!--
|
||||
<div class="inputItem" v-if="isItBound">
|
||||
@@ -269,6 +273,7 @@
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="closeParams.eCode"
|
||||
@input="handleCodeInput($event, 'eCode')"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -369,6 +374,7 @@
|
||||
showPassword
|
||||
type="password"
|
||||
v-model="params.pwd"
|
||||
@input="handlePasswordInput($event, 'pwd')"
|
||||
:placeholder="$t(`personal.pleaseEnter`)"
|
||||
></el-input>
|
||||
</div>
|
||||
@@ -378,6 +384,7 @@
|
||||
<el-input
|
||||
type="email"
|
||||
v-model="params.eCode"
|
||||
@input="handleCodeInput($event, 'paramsECode')"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -460,6 +467,7 @@
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="deleteAccountParams.eCode"
|
||||
@input="handleCodeInput($event, 'deleteAccountECode')"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
type="email"
|
||||
prefix-icon="el-icon-message"
|
||||
v-model="registerForm.email"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.Account`)"
|
||||
></el-input>
|
||||
@@ -40,6 +41,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="registerForm.emailCode"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -59,6 +61,7 @@
|
||||
:title="$t(`user.passwordPrompt`)"
|
||||
type="password"
|
||||
v-model="registerForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.password`)"
|
||||
@@ -73,6 +76,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="registerForm.confirmPassword"
|
||||
@input="handleConfirmPasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.confirmPassword`)"
|
||||
@@ -144,6 +148,7 @@
|
||||
type="email"
|
||||
prefix-icon="el-icon-message"
|
||||
v-model="registerForm.email"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.Account`)"
|
||||
></el-input>
|
||||
@@ -154,6 +159,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="registerForm.emailCode"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -173,6 +179,7 @@
|
||||
:title="$t(`user.passwordPrompt`)"
|
||||
type="password"
|
||||
v-model="registerForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.password`)"
|
||||
@@ -187,6 +194,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="registerForm.confirmPassword"
|
||||
@input="handleConfirmPasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.confirmPassword`)"
|
||||
@@ -233,6 +241,7 @@
|
||||
<script>
|
||||
import { getRegister, getRegisterCode } from "../../api/login";
|
||||
import { encryption } from "../../utils/fun";
|
||||
import { validatePassword, formatPasswordErrors } from "../../utils/passwordValidator";
|
||||
export default {
|
||||
data() {
|
||||
// const equalToPassword = (rule, value, callback) => {
|
||||
@@ -574,18 +583,15 @@ const path = this.$route.path;
|
||||
return;
|
||||
}
|
||||
|
||||
// 密码验证 8<=密码<=32 包含大小写字母、数字和特殊字符(!@#¥%……&*)
|
||||
// const regexPassword =
|
||||
// /^(?![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}$/; // 正则表达式
|
||||
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}$/;
|
||||
const PasswordIsValid = regexPassword.test(
|
||||
this.registerForm.password
|
||||
);
|
||||
if (!PasswordIsValid) {
|
||||
// 密码验证 - 使用优化后的详细校验逻辑
|
||||
const validation = validatePassword(this.registerForm.password);
|
||||
if (!validation.valid) {
|
||||
// 生成友好的逐项错误提示
|
||||
const errorMessage = formatPasswordErrors(validation.errors, this.$t.bind(this));
|
||||
this.$message({
|
||||
message: this.$t(`user.passwordFormat`),
|
||||
message: errorMessage,
|
||||
type: "error",
|
||||
duration: 5000, // 延长显示时间,因为错误信息可能有多条
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -628,6 +634,45 @@ const path = this.$route.path;
|
||||
handleClick() {
|
||||
this.$router.push(`/${this.lang}`);
|
||||
},
|
||||
/**
|
||||
* 去除输入值中的空格
|
||||
* @param {string} value - 输入值
|
||||
* @returns {string} 去除空格后的值
|
||||
*/
|
||||
removeSpaces(value) {
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\s/g, '');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
/**
|
||||
* 处理邮箱输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleEmailInput(value) {
|
||||
this.registerForm.email = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理验证码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleCodeInput(value) {
|
||||
this.registerForm.emailCode = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handlePasswordInput(value) {
|
||||
this.registerForm.password = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理确认密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleConfirmPasswordInput(value) {
|
||||
this.registerForm.confirmPassword = this.removeSpaces(value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<el-input
|
||||
prefix-icon="el-icon-user"
|
||||
v-model="loginForm.email"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t('user.Account')"
|
||||
></el-input>
|
||||
@@ -39,6 +40,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="loginForm.resetPwdCode"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -58,6 +60,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
@@ -71,11 +74,13 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.newPassword"
|
||||
@input="handleNewPasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
:placeholder="$t('user.newPassword')"
|
||||
></el-input>
|
||||
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="gCode" v-if="isItBound">
|
||||
<el-input
|
||||
@@ -150,6 +155,7 @@
|
||||
<el-input
|
||||
prefix-icon="el-icon-user"
|
||||
v-model="loginForm.email"
|
||||
@input="handleEmailInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t('user.Account')"
|
||||
></el-input>
|
||||
@@ -160,6 +166,7 @@
|
||||
type="text"
|
||||
prefix-icon="el-icon-chat-line-square"
|
||||
v-model="loginForm.resetPwdCode"
|
||||
@input="handleCodeInput($event)"
|
||||
autocomplete="off"
|
||||
:placeholder="$t(`user.verificationCode`)"
|
||||
></el-input>
|
||||
@@ -179,6 +186,7 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.password"
|
||||
@input="handlePasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
@@ -192,11 +200,13 @@
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="loginForm.newPassword"
|
||||
@input="handleNewPasswordInput($event)"
|
||||
prefix-icon="el-icon-unlock"
|
||||
autocomplete="off"
|
||||
showPassword
|
||||
:placeholder="$t('user.newPassword')"
|
||||
></el-input>
|
||||
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="gCode" v-if="isItBound">
|
||||
<el-input
|
||||
@@ -248,6 +258,7 @@ import { getResetPwd, getResetPwdCode } from "../../api/login";
|
||||
import { encryption } from "../../utils/fun";
|
||||
import { getEmailIfBind } from "../../api/personalCenter";
|
||||
import { Debounce,throttle }from "../../utils/publicMethods";
|
||||
import { validatePassword, formatPasswordErrors } from "../../utils/passwordValidator";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -573,16 +584,16 @@ export default {
|
||||
// }
|
||||
}
|
||||
|
||||
// 密码验证 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}$/; // 正则表达式
|
||||
const PasswordIsValid = regexPassword.test(this.loginForm.password);
|
||||
if (!PasswordIsValid) {
|
||||
// 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
|
||||
// 密码验证 - 使用优化后的详细校验逻辑
|
||||
const validation = validatePassword(this.loginForm.password);
|
||||
if (!validation.valid) {
|
||||
// 生成友好的逐项错误提示
|
||||
const errorMessage = formatPasswordErrors(validation.errors, this.$t.bind(this));
|
||||
this.$message({
|
||||
message: this.$t(`user.PasswordReminder`),
|
||||
message: errorMessage,
|
||||
type: "error",
|
||||
showClose: true,
|
||||
duration: 5000, // 延长显示时间,因为错误信息可能有多条
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -681,6 +692,45 @@ export default {
|
||||
handleClick() {
|
||||
this.$router.push(`/${this.lang}`);
|
||||
},
|
||||
/**
|
||||
* 去除输入值中的空格
|
||||
* @param {string} value - 输入值
|
||||
* @returns {string} 去除空格后的值
|
||||
*/
|
||||
removeSpaces(value) {
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\s/g, '');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
/**
|
||||
* 处理邮箱输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleEmailInput(value) {
|
||||
this.loginForm.email = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理验证码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleCodeInput(value) {
|
||||
this.loginForm.resetPwdCode = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handlePasswordInput(value) {
|
||||
this.loginForm.password = this.removeSpaces(value);
|
||||
},
|
||||
/**
|
||||
* 处理新密码输入,自动去除空格
|
||||
* @param {string} value - 输入值
|
||||
*/
|
||||
handleNewPasswordInput(value) {
|
||||
this.loginForm.newPassword = this.removeSpaces(value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Binary file not shown.
1
mining-pool/test/css/app-189e7968.5f96dc5b.css
Normal file
1
mining-pool/test/css/app-189e7968.5f96dc5b.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=google-site-verification content=pKAZogQ0NQ6L4j9-V58WJMjm7zYCFwkJXSJzWu9UDM8><meta name=robots content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"><meta name=googlebot content="index, follow"><meta name=googlebot-news content="index, follow"><meta name=bingbot content="index, follow"><link rel=alternate hreflang=zh href=https://m2pool.com/zh><link rel=alternate hreflang=en href=https://m2pool.com/en><link rel=alternate hreflang=x-default href=https://m2pool.com/en><meta property=og:title content="M2pool - Stable leading high-yield mining pool"><meta property=og:description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining"><meta property=og:url content=https://m2pool.com/en><meta property=og:site_name content=M2Pool><meta property=og:type content=website><meta property=og:image content=https://m2pool.com/logo.png><link rel=icon href=/favicon.ico><link rel=stylesheet href=//at.alicdn.com/t/c/font_4582735_7i8wfzc0art.css><title>M2pool - Stable leading high-yield mining pool</title><meta name=keywords content="M2Pool, cryptocurrency mining pool,entropyx, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿"><meta name=description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd"><script defer=defer src=/js/chunk-vendors-c0d76f48.f34181ba.js></script><script defer=defer src=/js/chunk-vendors-bc050c32.8062ab74.js></script><script defer=defer src=/js/chunk-vendors-3003db77.d0b93d36.js></script><script defer=defer src=/js/chunk-vendors-9d134daf.bb668c99.js></script><script defer=defer src=/js/chunk-vendors-96cecd74.a7d9b845.js></script><script defer=defer src=/js/chunk-vendors-c2f7d60e.3710fdc2.js></script><script defer=defer src=/js/chunk-vendors-89d5c698.2190b4ca.js></script><script defer=defer src=/js/chunk-vendors-377fed06.0e89b4b7.js></script><script defer=defer src=/js/chunk-vendors-c9ff040c.57bd8c18.js></script><script defer=defer src=/js/app-42f9d7e6.11a01d0a.js></script><script defer=defer src=/js/app-5c551db8.3726e21e.js></script><script defer=defer src=/js/app-45954fd3.1331a09e.js></script><script defer=defer src=/js/app-72600b29.ead62671.js></script><script defer=defer src=/js/app-5a0d40dd.b726f201.js></script><script defer=defer src=/js/app-113c6c50.ede228a3.js></script><link href=/css/chunk-vendors-bc050c32.6f97509c.css rel=stylesheet><link href=/css/app-189e7968.ecff2d4d.css rel=stylesheet></head><body><div id=app></div></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=google-site-verification content=pKAZogQ0NQ6L4j9-V58WJMjm7zYCFwkJXSJzWu9UDM8><meta name=robots content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"><meta name=googlebot content="index, follow"><meta name=googlebot-news content="index, follow"><meta name=bingbot content="index, follow"><link rel=alternate hreflang=zh href=https://m2pool.com/zh><link rel=alternate hreflang=en href=https://m2pool.com/en><link rel=alternate hreflang=x-default href=https://m2pool.com/en><meta property=og:title content="M2pool - Stable leading high-yield mining pool"><meta property=og:description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining"><meta property=og:url content=https://m2pool.com/en><meta property=og:site_name content=M2Pool><meta property=og:type content=website><meta property=og:image content=https://m2pool.com/logo.png><link rel=icon href=/favicon.ico><link rel=stylesheet href=//at.alicdn.com/t/c/font_4582735_7i8wfzc0art.css><title>M2pool - Stable leading high-yield mining pool</title><meta name=keywords content="M2Pool, cryptocurrency mining pool,entropyx, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿"><meta name=description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd"><script defer=defer src=/js/chunk-vendors-c0d76f48.f34181ba.js></script><script defer=defer src=/js/chunk-vendors-bc050c32.8062ab74.js></script><script defer=defer src=/js/chunk-vendors-3003db77.d0b93d36.js></script><script defer=defer src=/js/chunk-vendors-9d134daf.bb668c99.js></script><script defer=defer src=/js/chunk-vendors-96cecd74.a7d9b845.js></script><script defer=defer src=/js/chunk-vendors-c2f7d60e.3710fdc2.js></script><script defer=defer src=/js/chunk-vendors-89d5c698.2190b4ca.js></script><script defer=defer src=/js/chunk-vendors-377fed06.0e89b4b7.js></script><script defer=defer src=/js/chunk-vendors-c9ff040c.57bd8c18.js></script><script defer=defer src=/js/app-42f9d7e6.4bc16611.js></script><script defer=defer src=/js/app-5c551db8.e2a6dea3.js></script><script defer=defer src=/js/app-45954fd3.1331a09e.js></script><script defer=defer src=/js/app-72600b29.ca319ab4.js></script><script defer=defer src=/js/app-5a0d40dd.3a7bea46.js></script><script defer=defer src=/js/app-113c6c50.bea1eb5d.js></script><link href=/css/chunk-vendors-bc050c32.6f97509c.css rel=stylesheet><link href=/css/app-189e7968.5f96dc5b.css rel=stylesheet></head><body><div id=app></div></body></html>
|
||||
1
mining-pool/test/js/app-113c6c50.bea1eb5d.js
Normal file
1
mining-pool/test/js/app-113c6c50.bea1eb5d.js
Normal file
File diff suppressed because one or more lines are too long
1
mining-pool/test/js/app-42f9d7e6.4bc16611.js
Normal file
1
mining-pool/test/js/app-42f9d7e6.4bc16611.js
Normal file
File diff suppressed because one or more lines are too long
1
mining-pool/test/js/app-5a0d40dd.3a7bea46.js
Normal file
1
mining-pool/test/js/app-5a0d40dd.3a7bea46.js
Normal file
File diff suppressed because one or more lines are too long
1
mining-pool/test/js/app-5c551db8.e2a6dea3.js
Normal file
1
mining-pool/test/js/app-5c551db8.e2a6dea3.js
Normal file
File diff suppressed because one or more lines are too long
1
mining-pool/test/js/app-72600b29.ca319ab4.js
Normal file
1
mining-pool/test/js/app-72600b29.ca319ab4.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user