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 `
`;
}
/**
* 验证码组件
* @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,
};
}