import React, { useState } from 'react'; import { InputNumber, Select, Space, SpaceProps } from 'antd'; import type { InputNumberProps } from 'antd/es/input-number'; import type { SelectProps } from 'antd/es/select'; const { Option } = Select; export interface NumberUnitOption { label: React.ReactNode; value: string; } export interface NumberUnitValue { number?: number; unit?: string; } export type NumberWithUnitProps = SpaceProps & { value?: NumberUnitValue; onChange?: (next: NumberUnitValue) => void; numberClassName?: string; unitClassName?: string; className?: string; options: NumberUnitOption[]; defaultUnit?: string; defaultNumber?: number; numberProps?: Partial; selectProps?: Partial; }; const NumberWithUnit: React.FC = ({ value = {}, onChange, numberClassName, unitClassName, className, options, defaultUnit, defaultNumber, numberProps, selectProps, onBlur, ...rest // ← 这里会收到、style 等 }) => { const unit = value?.unit ?? defaultUnit ?? undefined; const [isComposing, setIsComposing] = useState(false); const triggerChange = (changed: Partial) => { onChange?.({ ...value, ...changed }); }; // 处理键盘输入 - 阻止非数字字符的输入 const handleKeyDown = (e: React.KeyboardEvent) => { // 如果正在使用输入法,不拦截 if (isComposing) return; // 允许的功能键 const allowedKeys = [ 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Tab', 'Enter', 'Escape', 'Home', 'End', ]; // 如果是功能键,允许 if (allowedKeys.includes(e.key)) return; // 如果是 Ctrl/Cmd 组合键(如 Ctrl+A, Ctrl+C, Ctrl+V),允许 if (e.ctrlKey || e.metaKey) return; // 如果不是数字,阻止输入 if (!/^\d$/.test(e.key)) { e.preventDefault(); } }; // 处理粘贴 - 过滤粘贴内容中的非数字字符 const handlePaste = (e: React.ClipboardEvent) => { e.preventDefault(); // 获取剪贴板内容 const pastedText = e.clipboardData.getData('text'); // 只保留数字 const cleanedText = pastedText.replace(/\D/g, ''); if (cleanedText) { const numValue = parseInt(cleanedText, 10); triggerChange({ number: numValue }); } }; // 处理输入法开始 const handleCompositionStart = () => { setIsComposing(true); }; // 处理输入法结束 - 过滤输入法输入的非数字字符 const handleCompositionEnd = ( e: React.CompositionEvent ) => { setIsComposing(false); // 获取输入的内容并过滤 const inputValue = e.currentTarget.value; const cleanedValue = inputValue.replace(/\D/g, ''); if (cleanedValue) { const numValue = parseInt(cleanedValue, 10); triggerChange({ number: numValue }); } else { // 如果过滤后没有有效数字,清空 triggerChange({ number: undefined }); } }; return ( <> triggerChange({ number: typeof n === 'number' ? n : undefined }) } min={0} precision={0} parser={(value) => value?.replace(/\D/g, '') || ''} keyboard={true} onKeyDown={handleKeyDown} onPaste={handlePaste} onCompositionStart={handleCompositionStart} onCompositionEnd={handleCompositionEnd} {...numberProps} defaultValue={defaultNumber} onBlur={onBlur} /> ); }; export default NumberWithUnit;