123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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 `<text
- x="${x}"
- y="${y}"
- font-size="${fontSize}"
- font-family="Arial, sans-serif"
- font-weight="bold"
- text-anchor="middle"
- fill="${fill}"
- style="letter-spacing: 1px;"
- >${char}</text>`;
- }).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 += `<path
- d="M ${startX} ${startY} C ${control1X} ${control1Y}, ${control2X} ${control2Y}, ${endX} ${endY}"
- stroke="rgba(255, 255, 255, ${0.2 + Math.random() * 0.3})"
- stroke-width="${1 + Math.random()}"
- fill="none"
- />`;
- }
-
- // 添加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 += `<circle
- cx="${x}"
- cy="${y}"
- r="${radius}"
- fill="rgba(255, 255, 255, ${0.4 + Math.random() * 0.4})"
- />`;
- }
-
- return `
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="${width}"
- height="${height}"
- viewBox="0 0 ${width} ${height}"
- style="background-color: rgba(55, 65, 81, 0.5); border-radius: 6px; border: 1px solid rgba(107, 114, 128, 0.5);"
- >
- ${lines}
- ${textElements}
- </svg>
- `;
- }
-
- /**
- * 验证码组件
- * @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,
- };
- }
|