diff --git a/mining-pool/dist.zip b/mining-pool/dist.zip index 4f2092e..f998069 100644 Binary files a/mining-pool/dist.zip and b/mining-pool/dist.zip differ diff --git a/mining-pool/src/components/PasswordStrengthIndicator.vue b/mining-pool/src/components/PasswordStrengthIndicator.vue new file mode 100644 index 0000000..eb29946 --- /dev/null +++ b/mining-pool/src/components/PasswordStrengthIndicator.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/mining-pool/src/i18n/userLang.js b/mining-pool/src/i18n/userLang.js index b70b2cf..66b64b1 100644 --- a/mining-pool/src/i18n/userLang.js +++ b/mining-pool/src/i18n/userLang.js @@ -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" } } \ No newline at end of file diff --git a/mining-pool/src/utils/passwordValidator.js b/mining-pool/src/utils/passwordValidator.js new file mode 100644 index 0000000..8380281 --- /dev/null +++ b/mining-pool/src/utils/passwordValidator.js @@ -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} 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} 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') +} diff --git a/mining-pool/src/views/login/login.vue b/mining-pool/src/views/login/login.vue index 77b4b46..7265fed 100644 --- a/mining-pool/src/views/login/login.vue +++ b/mining-pool/src/views/login/login.vue @@ -30,6 +30,7 @@ @@ -38,6 +39,7 @@ @@ -132,6 +135,7 @@ @@ -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); + }, }, }; diff --git a/mining-pool/src/views/personalCenter/securitySetting/index.js b/mining-pool/src/views/personalCenter/securitySetting/index.js index faa549e..dba998d 100644 --- a/mining-pool/src/views/personalCenter/securitySetting/index.js +++ b/mining-pool/src/views/personalCenter/securitySetting/index.js @@ -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; + } + }, diff --git a/mining-pool/src/views/personalCenter/securitySetting/index.vue b/mining-pool/src/views/personalCenter/securitySetting/index.vue index 10deece..8847950 100644 --- a/mining-pool/src/views/personalCenter/securitySetting/index.vue +++ b/mining-pool/src/views/personalCenter/securitySetting/index.vue @@ -202,6 +202,7 @@ @@ -223,6 +224,7 @@ type="password" showPassword v-model="changePasswordParams.password" + @input="handlePasswordInput($event, 'password')" :placeholder="$t(`personal.pleaseEnter`)" >

@@ -236,8 +238,10 @@ type="password" showPassword v-model="newPassword" + @input="handlePasswordInput($event, 'newPassword')" :placeholder="$t(`personal.pleaseEnter`)" > +