278 lines
7.4 KiB
C#
278 lines
7.4 KiB
C#
|
|
using System;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
|
|||
|
|
namespace Kill.UI.Components
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验证码输入组件
|
|||
|
|
/// 使用方式:
|
|||
|
|
/// 1. 在场景中创建一个空物体,挂载此脚本
|
|||
|
|
/// 2. 创建n个Text作为验证码显示位
|
|||
|
|
/// 3. 创建一个InputField作为隐藏输入框(可设置为透明或移出屏幕外)
|
|||
|
|
/// 4. 在Inspector中配置digitTexts和inputField
|
|||
|
|
/// </summary>
|
|||
|
|
public class VerificationCodeInput : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
[Header("验证码配置")]
|
|||
|
|
[Tooltip("验证码位数")]
|
|||
|
|
[Range(1, 10)]
|
|||
|
|
public int codeLength = 6;
|
|||
|
|
|
|||
|
|
[Tooltip("是否自动验证 - 输入完成后自动触发OnCodeCompleted事件")]
|
|||
|
|
public bool autoSubmit = true;
|
|||
|
|
|
|||
|
|
[Tooltip("是否只允许数字")]
|
|||
|
|
public bool numericOnly = true;
|
|||
|
|
|
|||
|
|
[Header("UI引用")]
|
|||
|
|
[Tooltip("验证码显示文本数组,按顺序从左到右")]
|
|||
|
|
public Text[] digitTexts;
|
|||
|
|
|
|||
|
|
[Tooltip("隐藏的输入框(用于接收用户输入)")]
|
|||
|
|
public InputField inputField;
|
|||
|
|
|
|||
|
|
[Header("样式配置")]
|
|||
|
|
[Tooltip("未输入时的占位字符")]
|
|||
|
|
public string placeholderChar = "-";
|
|||
|
|
|
|||
|
|
[Tooltip("输入完成后的字符(如需要隐藏可设为*)")]
|
|||
|
|
public string displayChar = "";
|
|||
|
|
|
|||
|
|
public event Action<string> OnCodeCompleted;
|
|||
|
|
public event Action<string> OnCodeChanged;
|
|||
|
|
|
|||
|
|
private string currentCode = "";
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
Initialize();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnEnable()
|
|||
|
|
{
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.onValueChanged.AddListener(HandleInputChanged);
|
|||
|
|
inputField.onEndEdit.AddListener(HandleEndEdit);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDisable()
|
|||
|
|
{
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.onValueChanged.RemoveListener(HandleInputChanged);
|
|||
|
|
inputField.onEndEdit.RemoveListener(HandleEndEdit);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Start()
|
|||
|
|
{
|
|||
|
|
RefreshDisplay();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化组件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Initialize()
|
|||
|
|
{
|
|||
|
|
if (inputField == null)
|
|||
|
|
{
|
|||
|
|
inputField = GetComponentInChildren<InputField>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (digitTexts == null || digitTexts.Length == 0)
|
|||
|
|
{
|
|||
|
|
digitTexts = GetComponentsInChildren<Text>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (digitTexts != null && digitTexts.Length > 0)
|
|||
|
|
{
|
|||
|
|
codeLength = Mathf.Min(codeLength, digitTexts.Length);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.characterLimit = codeLength;
|
|||
|
|
inputField.contentType = numericOnly
|
|||
|
|
? InputField.ContentType.IntegerNumber
|
|||
|
|
: InputField.ContentType.Alphanumeric;
|
|||
|
|
|
|||
|
|
var inputText = inputField.textComponent;
|
|||
|
|
if (inputText != null)
|
|||
|
|
{
|
|||
|
|
inputText.color = new Color(1, 1, 1, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (inputField.placeholder != null)
|
|||
|
|
{
|
|||
|
|
var placeholder = inputField.placeholder.GetComponent<Text>();
|
|||
|
|
if (placeholder != null)
|
|||
|
|
{
|
|||
|
|
placeholder.color = new Color(1, 1, 1, 0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理输入变化
|
|||
|
|
/// </summary>
|
|||
|
|
private void HandleInputChanged(string value)
|
|||
|
|
{
|
|||
|
|
if (numericOnly)
|
|||
|
|
{
|
|||
|
|
value = FilterNumeric(value);
|
|||
|
|
if (inputField != null && inputField.text != value)
|
|||
|
|
{
|
|||
|
|
inputField.text = value;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (value.Length > codeLength)
|
|||
|
|
{
|
|||
|
|
value = value.Substring(0, codeLength);
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.text = value;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
currentCode = value;
|
|||
|
|
RefreshDisplay();
|
|||
|
|
|
|||
|
|
OnCodeChanged?.Invoke(currentCode);
|
|||
|
|
|
|||
|
|
if (autoSubmit && currentCode.Length == codeLength)
|
|||
|
|
{
|
|||
|
|
Debug.Log("自动提交");
|
|||
|
|
OnCodeCompleted?.Invoke(currentCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理结束编辑(用户按下回车或失去焦点)
|
|||
|
|
/// </summary>
|
|||
|
|
private void HandleEndEdit(string value)
|
|||
|
|
{
|
|||
|
|
if (!autoSubmit && currentCode.Length == codeLength)
|
|||
|
|
{
|
|||
|
|
OnCodeCompleted?.Invoke(currentCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 刷新显示
|
|||
|
|
/// </summary>
|
|||
|
|
private void RefreshDisplay()
|
|||
|
|
{
|
|||
|
|
if (digitTexts == null) return;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < digitTexts.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (digitTexts[i] == null) continue;
|
|||
|
|
|
|||
|
|
if (i < currentCode.Length)
|
|||
|
|
{
|
|||
|
|
char c = currentCode[i];
|
|||
|
|
digitTexts[i].text = string.IsNullOrEmpty(displayChar) ? c.ToString() : displayChar;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
digitTexts[i].text = placeholderChar;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 过滤非数字字符
|
|||
|
|
/// </summary>
|
|||
|
|
private string FilterNumeric(string input)
|
|||
|
|
{
|
|||
|
|
var result = "";
|
|||
|
|
foreach (char c in input)
|
|||
|
|
{
|
|||
|
|
if (char.IsDigit(c))
|
|||
|
|
{
|
|||
|
|
result += c;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取当前验证码
|
|||
|
|
/// </summary>
|
|||
|
|
public string GetCode()
|
|||
|
|
{
|
|||
|
|
return currentCode;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置验证码(用于自动填充等)
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetCode(string code)
|
|||
|
|
{
|
|||
|
|
if (code == null)
|
|||
|
|
{
|
|||
|
|
code = "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (code.Length > codeLength)
|
|||
|
|
{
|
|||
|
|
code = code.Substring(0, codeLength);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
currentCode = code;
|
|||
|
|
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.text = currentCode;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
RefreshDisplay();
|
|||
|
|
OnCodeChanged?.Invoke(currentCode);
|
|||
|
|
|
|||
|
|
if (currentCode.Length == codeLength)
|
|||
|
|
{
|
|||
|
|
OnCodeCompleted?.Invoke(currentCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空输入
|
|||
|
|
/// </summary>
|
|||
|
|
public void Clear()
|
|||
|
|
{
|
|||
|
|
currentCode = "";
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.text = "";
|
|||
|
|
}
|
|||
|
|
RefreshDisplay();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 让输入框获得焦点
|
|||
|
|
/// </summary>
|
|||
|
|
public void Focus()
|
|||
|
|
{
|
|||
|
|
if (inputField != null)
|
|||
|
|
{
|
|||
|
|
inputField.ActivateInputField();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 检查验证码是否输入完整
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsComplete()
|
|||
|
|
{
|
|||
|
|
return currentCode.Length == codeLength;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|