import { ref, computed, reactive } from 'vue'; /** * 生成随机字符串 * @param length 字符串长度 * @returns 随机字符串 */ function generateRandomString(length: number): string { // 使用更简单的字符集,避免混淆字符 const characters = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; let result = ''; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * characters.length)); } return result; } /** * 为验证码文本生成SVG * @param text 验证码文本 * @returns SVG 字符串 */ function generateCaptchaSvg(text: string): string { const width = 150; const height = 50; const fontSize = 30; const letters = text.split(''); // 文字元素(确保可读性) const textElements = letters.map((char, index) => { const x = (width / text.length) * (index + 0.5); const y = height / 2 + fontSize / 3; const fill = `hsl(${Math.random() * 360}, 70%, 80%)`; // 提高亮度,增加可读性 return `${char}`; }).join(''); // 生成干扰线 let lines = ''; // 添加2-4条随机曲线 for (let i = 0; i < 3; i++) { const startX = Math.random() * width * 0.2; const startY = Math.random() * height; const endX = width - Math.random() * width * 0.2; const endY = Math.random() * height; const control1X = width * 0.3 + Math.random() * width * 0.2; const control1Y = Math.random() * height; const control2X = width * 0.5 + Math.random() * width * 0.2; const control2Y = Math.random() * height; // 使用贝塞尔曲线创建更自然的干扰线 lines += ``; } // 添加5-10个随机点 for (let i = 0; i < 8; i++) { const x = Math.random() * width; const y = Math.random() * height; const radius = 1 + Math.random() * 2; lines += ``; } return ` ${lines} ${textElements} `; } /** * 验证码组件 * @param length 验证码长度 * @returns 验证码相关状态和方法 */ export function useCaptcha(length: number = 4) { // 使用状态对象存储所有相关状态 const state = reactive({ captchaText: '', // 验证码文本 userInput: '', // 用户输入 svgHtml: '', // 验证码SVG error: '', // 错误信息 }); /** * 生成新的验证码 */ const generateCaptcha = () => { // 生成新的验证码文本 const newText = generateRandomString(length); // 更新状态 state.captchaText = newText; state.userInput = ''; // 清空用户输入 state.error = ''; // 清除错误 // 生成验证码SVG state.svgHtml = generateCaptchaSvg(newText); }; /** * 验证用户输入 * @returns 是否验证通过 */ const validateCaptcha = (): boolean => { // 确保userInput和captchaText都被处理为大写字符串进行比较 const normalizedInput = (state.userInput || '').toUpperCase().trim(); const normalizedCaptcha = (state.captchaText || '').toUpperCase().trim(); if (!normalizedInput) { state.error = '请输入验证码'; return false; } // 验证用户输入是否与验证码匹配 const isValid = normalizedInput === normalizedCaptcha; if (!isValid) { state.error = '验证码错误'; generateCaptcha(); // 错误后自动刷新 return false; } state.error = ''; return true; }; /** * 获取当前验证码文本 */ const getCurrentCaptcha = () => { return state.captchaText; }; // 初始化生成第一个验证码 generateCaptcha(); return { state, userInput: computed({ get: () => state.userInput, set: (value) => { state.userInput = value; } }), captchaSvg: computed(() => state.svgHtml), error: computed(() => state.error), generateCaptcha, validateCaptcha, getCurrentCaptcha, }; }