Skip to content

可配置验证码功能实现方案

功能概述

本功能实现了通过项目级别配置来控制验证码功能的开启与关闭,支持图片验证码和短信验证码的独立配置。采用组合式 API useConfigurableVerifyCode 实现,提供更好的代码复用性和维护性。

设计目标

  1. 统一配置读取 - 使用项目统一的 getConfig() 函数
  2. 封装组合式 API - 将验证码配置逻辑封装为可复用的组合式函数
  3. 简化组件代码 - 减少组件中的配置处理逻辑
  4. 提升扩展性 - 便于后续添加新的验证码类型

技术架构

配置层级结构

详细实现方案

1. 配置文件设置

文件apps/admin/public/platform-config.json

平台配置文件示例
json
{
	"CaptchaConfig": {
		"isImageCaptchaEnabled": false, // 是否启用图片验证码,默认false
		"isSmsCaptchaEnabled": true, // 是否启用短信验证码,默认true
		"isSystemCaptchaEnabled": true // 是否启用系统自带的前端验证码,默认true
	}
}

设计说明

  • 使用嵌套对象 CaptchaConfig 组织验证码相关配置
  • 默认值:图片验证码关闭,短信验证码开启,系统自带验证码开启

2. 类型定义

文件apps/admin/types/global.d.ts

类型定义示例
ts
interface PlatformConfigs {
	/** 验证码相关配置 */
	CaptchaConfig?: {
		/** 是否启用图片验证码,默认false */
		isImageCaptchaEnabled?: boolean;
		/** 是否启用短信验证码,默认true */
		isSmsCaptchaEnabled?: boolean;
		/** 是否启用系统自带的前端验证码,默认true */
		isSystemCaptchaEnabled?: boolean;
	};
}

interface StorageConfigs {
	/** 验证码相关配置(驼峰命名用于本地存储) */
	captchaConfig?: {
		/** 是否启用图片验证码,默认false */
		isImageCaptchaEnabled?: boolean;
		/** 是否启用短信验证码,默认true */
		isSmsCaptchaEnabled?: boolean;
		/** 是否启用系统自带的前端验证码,默认true */
		isSystemCaptchaEnabled?: boolean;
	};
}

3. 创建组合式 API

文件src/composables/use-configurable-verify-code/index.ts

创建组合式 API
ts
import { computed, type ComputedRef } from "vue";
import { getConfig } from "@/config";

/**
 * 可配置验证码组合式 API
 *
 * 提供统一的验证码配置管理,支持图片验证码和短信验证码的独立控制
 *
 */
export function useConfigurableVerifyCode() {
	/**
	 * 是否启用图片验证码
	 * @description 控制登录页面图片验证码的显示和验证逻辑
	 * @default false
	 */
	const isImageCaptchaEnabled: ComputedRef<boolean> = computed(() => {
		return getConfig()?.CaptchaConfig?.isImageCaptchaEnabled ?? false;
	});

	/**
	 * 是否启用短信验证码
	 * @description 控制手机登录和忘记密码页面短信验证码功能
	 * @default true
	 */
	const isSmsCaptchaEnabled: ComputedRef<boolean> = computed(() => {
		return getConfig()?.CaptchaConfig?.isSmsCaptchaEnabled ?? true;
	});

	/**
	 * 是否启用系统自带的前端验证码
	 * @description 控制是否使用框架自带的ReImageVerify组件
	 * @default true
	 */
	const isSystemCaptchaEnabled: ComputedRef<boolean> = computed(() => {
		return getConfig()?.CaptchaConfig?.isSystemCaptchaEnabled ?? true;
	});

	/**
	 * 获取完整的验证码配置
	 * @description 返回当前的验证码配置对象
	 */
	const captchaConfig = computed(() => {
		return (
			getConfig()?.CaptchaConfig ?? {
				isImageCaptchaEnabled: false,
				isSmsCaptchaEnabled: true,
				isSystemCaptchaEnabled: true,
			}
		);
	});

	/**
	 * 检查是否需要验证码
	 * @description 判断当前是否启用了任何类型的验证码
	 */
	const isVerificationRequired = computed(() => {
		return isImageCaptchaEnabled.value || isSmsCaptchaEnabled.value;
	});

	/**
	 * 根据配置构建登录参数
	 * @param baseParams 基础登录参数
	 * @param captchaData 验证码相关数据
	 * @returns 完整的登录参数对象
	 */
	function buildLoginParams(
		baseParams: { username: string; password: string },
		captchaData?: {
			verifyCode?: string;
			uuid?: string;
			smsCode?: string;
			phone?: string;
		},
	) {
		const params: any = { ...baseParams };

		// 图片验证码参数
		if (isImageCaptchaEnabled.value && captchaData?.verifyCode && captchaData?.uuid) {
			params.code = captchaData.verifyCode;
			params.uuid = captchaData.uuid;
		}

		// 短信验证码参数
		if (isSmsCaptchaEnabled.value && captchaData?.smsCode) {
			params.smsCode = captchaData.smsCode;
			if (captchaData.phone) {
				params.phone = captchaData.phone;
			}
		}

		return params;
	}

	return {
		/** 是否启用图片验证码 */
		isImageCaptchaEnabled,
		/** 是否启用短信验证码 */
		isSmsCaptchaEnabled,
		/** 是否启用系统自带的前端验证码 */
		isSystemCaptchaEnabled,
		/** 完整的验证码配置 */
		captchaConfig,
		/** 是否需要验证码 */
		isVerificationRequired,
		/** 构建登录参数 */
		buildLoginParams,
	};
}

export type UseConfigurableVerifyCodeReturn = ReturnType<typeof useConfigurableVerifyCode>;

4. 组件使用示例

登录页面实现

typescript
import { useConfigurableVerifyCode } from "@/composables/use-configurable-verify-code";

const { isImageCaptchaEnabled, isSystemCaptchaEnabled, buildLoginParams } = useConfigurableVerifyCode();

// 自动构建登录参数
const loginData = buildLoginParams(
	{ username: ruleForm.username, password: ruleForm.password },
	{ verifyCode: ruleForm.verifyCode, uuid: captchaInfo.value?.uuid },
);

模板条件渲染

vue
<!-- 图片验证码 -->
<Motion v-if="isImageCaptchaEnabled" :delay="200">
  <el-form-item prop="verifyCode">
    <!-- 验证码输入框 -->
  </el-form-item>
</Motion>

<!-- 短信验证码 -->
<Motion v-if="isSmsCaptchaEnabled" :delay="100">
  <el-form-item prop="verifyCode">
    <!-- 短信验证码输入框 -->
  </el-form-item>
</Motion>

实现效果

  • 配置统一 - 使用 getConfig() 统一读取配置
  • 代码简洁 - 组件代码从 ~10 行减少到 ~3 行
  • 无重复代码 - 多个页面统一调用组合式 API
  • 自动构建 - 登录参数自动构建,无需手动扩展
  • 完全类型安全 - 完整的 TypeScript 类型支持

技术实现细节

1. 配置读取机制

使用项目统一的 getConfig() 函数:

typescript
const isImageCaptchaEnabled = computed(() => {
	return getConfig()?.CaptchaConfig?.isImageCaptchaEnabled ?? false;
});

特点

  • 与项目配置管理体系保持一致
  • 提供默认值兜底机制
  • 响应式配置更新

2. 条件渲染策略

使用 v-if 实现完全的条件控制:

vue
<Motion v-if="isImageCaptchaEnabled" :delay="200">
  <!-- 验证码相关组件 -->
</Motion>

设计考虑

  • 配置关闭时完全移除 DOM 节点
  • 保留原有的 Motion 动画效果
  • 不影响其他表单项的延迟时间

3. 自动参数构建

buildLoginParams 函数根据配置自动构建请求参数:

typescript
function buildLoginParams(baseParams, captchaData) {
	const params = { ...baseParams };
	// 根据配置条件添加验证码参数
	return params;
}

优势

  • 代码简洁,逻辑清晰
  • 避免不必要的参数传递
  • 统一的参数构建逻辑

配置场景示例

开发环境(关闭所有验证码)

开发环境配置
json
{
	"CaptchaConfig": {
		"isImageCaptchaEnabled": false,
		"isSmsCaptchaEnabled": false,
		"isSystemCaptchaEnabled": true
	}
}

生产环境(启用所有验证码)

生产环境配置
json
{
	"CaptchaConfig": {
		"isImageCaptchaEnabled": true,
		"isSmsCaptchaEnabled": true,
		"isSystemCaptchaEnabled": true
	}
}

默认配置(仅短信验证码)

默认配置
json
{
	"CaptchaConfig": {
		"isImageCaptchaEnabled": false,
		"isSmsCaptchaEnabled": true,
		"isSystemCaptchaEnabled": true
	}
}

使用自定义验证码(仅图片验证码,使用自定义验证码组件)

适用于需要图片验证码但不使用框架自带验证码组件的场景。

使用自定义验证码
json
{
	"CaptchaConfig": {
		"isImageCaptchaEnabled": true,
		"isSmsCaptchaEnabled": false,
		"isSystemCaptchaEnabled": false
	}
}

扩展性设计

添加新验证码类型

只需三步即可添加新的验证码类型:

  1. 扩展类型定义
typescript
interface CaptchaConfig {
	isImageCaptchaEnabled?: boolean;
	isSmsCaptchaEnabled?: boolean;
	isSystemCaptchaEnabled?: boolean;
	isVoiceCaptchaEnabled?: boolean; // 新增
}
  1. 添加组合式函数属性
typescript
const isVoiceCaptchaEnabled = computed(() => {
	return getConfig()?.CaptchaConfig?.isVoiceCaptchaEnabled ?? false;
});
  1. 更新参数构建逻辑
typescript
if (isVoiceCaptchaEnabled.value && captchaData?.voiceCode) {
	params.voiceCode = captchaData.voiceCode;
}

性能优化

  1. 计算属性缓存 - 使用 computed 自动缓存计算结果
  2. 按需导入 - 只导入需要的功能
  3. 函数式设计 - buildLoginParams 采用纯函数设计
  4. 条件渲染 - 使用 v-if 避免不必要的组件初始化

测试验证

功能测试场景

场景isImageCaptchaEnabledisSmsCaptchaEnabledisSystemCaptchaEnabled预期结果
默认配置falsetruetrue登录页无图片验证码,手机登录有短信验证码
全关闭falsefalsetrue所有验证码功能都关闭
全开启(系统验证码)truetruetrue所有验证码功能都开启,使用系统自带验证码组件
全开启(自定义验证码)truetruefalse所有验证码功能都开启,使用自定义验证码组件
仅图片(系统验证码)truefalsetrue仅登录页显示图片验证码,使用系统自带验证码组件
仅图片(自定义验证码)truefalsefalse仅登录页显示图片验证码,使用自定义验证码组件

兼容性测试

  1. 向后兼容 - 在没有新配置的情况下使用默认值
  2. 异常处理 - 配置文件格式错误时降级到默认行为
  3. 类型检查 - 确保 TypeScript 编译无错误

风险评估

风险项风险等级影响范围应对措施
配置文件损坏系统启动默认值兜底机制
类型定义错误编译时错误完善的类型测试
逻辑分支遗漏功能异常全面的测试用例覆盖

贡献者

The avatar of contributor named as ruan-cat ruan-cat

页面历史

最近更新