| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- import React, { useEffect, useState, useRef } from 'react';
- import styles from './styles.module.scss';
- import { APP_OPT_LENGTH } from '@constants/app';
- const MobileCodeInput = ({ length = APP_OPT_LENGTH, onChange, className = '', onFocus, onBlur }) => {
- const inputLength = Number.isInteger(length) && length > 0 ? length : APP_OPT_LENGTH;
- // 初始化一个长度为6的空字符串数组
- const [code, setCode] = useState(new Array(inputLength).fill(''));
- const [active, setActive] = useState(false);
- // 用于存储每个输入框的DOM引用,方便控制焦点
- const inputRefs = useRef([]);
- useEffect(() => {
- setCode(new Array(inputLength).fill(''));
- setActive(false);
- inputRefs.current = inputRefs.current.slice(0, inputLength);
- }, [inputLength]);
- // 新增:处理聚焦
- const handleFocus = (index) => {
- onFocus?.(index); // 传回聚焦的输入框索引
- };
- // 新增:处理失焦
- const handleBlur = () => {
- onBlur?.();
- };
- const handleCodeChanged = (newCode) => {
- const completed = newCode.every((char) => char !== '') && newCode.length === inputLength;
- setActive(completed);
- onChange?.(newCode.join(''));
- }
- // 处理输入变化
- const handleChange = (index, e) => {
- const value = e.target.value;
- // 仅允许输入数字
- if (!/^\d*$/.test(value)) return;
- const newCode = [...code];
- // 如果用户快速输入覆盖了原数字,取最后一位
- newCode[index] = value.substring(value.length - 1);
- setCode(newCode);
- // 如果当前输入框有值,且不是最后一个,则焦点自动移向下一个
- if (value && index < inputLength - 1) {
- inputRefs.current[index + 1]?.focus();
- }
- // 检查是否所有格子都已填满,如果是则触发回调
- handleCodeChanged(newCode);
- };
- // 处理键盘按键事件(主要处理 Backspace 逻辑)
- const handleKeyDown = (index, e) => {
- // 当按下退格键,且当前格子为空时,焦点自动移向上一个格子
- if (e.key === 'Backspace' && !code[index] && index > 0) {
- inputRefs.current[index - 1]?.focus();
- }
- };
- // 处理粘贴事件
- const handlePaste = (e) => {
- e.preventDefault();
- // 获取粘贴板中的文本
- const pastedData = e.clipboardData.getData('text/plain').trim();
- // 过滤非数字字符,并截取到指定长度
- const cleanData = pastedData.replace(/\D/g, '').slice(0, inputLength);
- if (!cleanData) return;
- const newCode = new Array(inputLength).fill('');
- const pastedArray = cleanData.split('');
- // 将粘贴的数据分配到数组中
- pastedArray.forEach((char, i) => {
- newCode[i] = char;
- });
- setCode(newCode);
- // 计算粘贴后焦点应该停留在哪个输入框
- const focusIndex = pastedArray.length < inputLength ? pastedArray.length : inputLength - 1;
- inputRefs.current[focusIndex]?.focus();
- // 如果粘贴的长度刚好等于所需长度,触发回调
- handleCodeChanged(newCode)
- };
- return (
- <div className={`${styles.container} ${className}`}>
- {
- code.map((value, index) => (
- <input
- key={index}
- // 将每个 input 的引用保存到 inputRefs 数组中
- ref={(el) => (inputRefs.current[index] = el)}
- type="text"
- inputMode="numeric" // 在移动端唤起数字键盘
- value={value}
- onChange={(e) => handleChange(index, e)}
- onKeyDown={(e) => handleKeyDown(index, e)}
- onPaste={handlePaste}
- className={`${styles.input_box} ${active ? styles.active : ''}`}
- onFocus={() => handleFocus(index)} // 新增
- onBlur={handleBlur} // 新增
- />
- ))}
- </div>
- );
- };
- export default MobileCodeInput;
|