147 lines
3.6 KiB
TypeScript
147 lines
3.6 KiB
TypeScript
import { Ionicons } from '@expo/vector-icons';
|
||
import React from 'react';
|
||
import {
|
||
Dimensions,
|
||
StyleSheet,
|
||
Text,
|
||
TouchableOpacity,
|
||
View,
|
||
} from 'react-native';
|
||
|
||
interface NumberKeyboardProps {
|
||
onNumberPress: (number: string) => void;
|
||
onDeletePress: () => void;
|
||
onDecimalPress: () => void;
|
||
hasDecimal?: boolean;
|
||
maxLength?: number;
|
||
currentValue?: string;
|
||
}
|
||
|
||
const { width } = Dimensions.get('window');
|
||
const keyWidth = (width - 80) / 3; // 减去左右边距和间隙
|
||
|
||
export default function NumberKeyboard({
|
||
onNumberPress,
|
||
onDeletePress,
|
||
onDecimalPress,
|
||
hasDecimal = false,
|
||
maxLength = 6,
|
||
currentValue = '',
|
||
}: NumberKeyboardProps) {
|
||
const handleNumberPress = (number: string) => {
|
||
if (currentValue.length >= maxLength) return;
|
||
// 防止输入多个0开头
|
||
if (currentValue === '0' && number === '0') return;
|
||
// 如果当前是0,输入非0数字时替换
|
||
if (currentValue === '0' && number !== '0') {
|
||
// 这里不需要replace,直接传递number即可
|
||
return;
|
||
}
|
||
onNumberPress(number);
|
||
};
|
||
|
||
const handleDecimalPress = () => {
|
||
if (hasDecimal || currentValue.includes('.')) return;
|
||
onDecimalPress();
|
||
};
|
||
|
||
const renderKey = (
|
||
value: string,
|
||
onPress: () => void,
|
||
style?: any,
|
||
textStyle?: any,
|
||
disabled?: boolean
|
||
) => (
|
||
<TouchableOpacity
|
||
style={[
|
||
styles.key,
|
||
{ width: keyWidth },
|
||
style,
|
||
disabled && styles.keyDisabled
|
||
]}
|
||
onPress={onPress}
|
||
disabled={disabled}
|
||
activeOpacity={0.7}
|
||
>
|
||
{value === 'delete' ? (
|
||
<Ionicons name="backspace-outline" size={24} color="#374151" />
|
||
) : (
|
||
<Text style={[styles.keyText, textStyle, disabled && styles.keyTextDisabled]}>
|
||
{value}
|
||
</Text>
|
||
)}
|
||
</TouchableOpacity>
|
||
);
|
||
|
||
return (
|
||
<View style={styles.container}>
|
||
<View style={styles.row}>
|
||
{renderKey('1', () => handleNumberPress('1'))}
|
||
{renderKey('2', () => handleNumberPress('2'))}
|
||
{renderKey('3', () => handleNumberPress('3'))}
|
||
</View>
|
||
<View style={styles.row}>
|
||
{renderKey('4', () => handleNumberPress('4'))}
|
||
{renderKey('5', () => handleNumberPress('5'))}
|
||
{renderKey('6', () => handleNumberPress('6'))}
|
||
</View>
|
||
<View style={styles.row}>
|
||
{renderKey('7', () => handleNumberPress('7'))}
|
||
{renderKey('8', () => handleNumberPress('8'))}
|
||
{renderKey('9', () => handleNumberPress('9'))}
|
||
</View>
|
||
<View style={styles.row}>
|
||
{renderKey(
|
||
'.',
|
||
handleDecimalPress,
|
||
undefined,
|
||
undefined,
|
||
hasDecimal || currentValue.includes('.')
|
||
)}
|
||
{renderKey('0', () => handleNumberPress('0'))}
|
||
{renderKey('delete', onDeletePress)}
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
backgroundColor: '#F9FAFB',
|
||
paddingVertical: 20,
|
||
paddingHorizontal: 20,
|
||
borderTopWidth: 1,
|
||
borderTopColor: '#E5E7EB',
|
||
},
|
||
row: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 12,
|
||
},
|
||
key: {
|
||
height: 50,
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 12,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 1 },
|
||
shadowOpacity: 0.05,
|
||
shadowRadius: 2,
|
||
elevation: 1,
|
||
borderWidth: 1,
|
||
borderColor: '#E5E7EB',
|
||
},
|
||
keyDisabled: {
|
||
backgroundColor: '#F3F4F6',
|
||
opacity: 0.5,
|
||
},
|
||
keyText: {
|
||
fontSize: 20,
|
||
fontWeight: '600',
|
||
color: '#374151',
|
||
},
|
||
keyTextDisabled: {
|
||
color: '#9CA3AF',
|
||
},
|
||
}); |