reactnative下拉选择
引用
<FilterDropdownlabel={""}options={this.state.recordListType}value={this.state.recordType}onChange={(item) => this.handleRecordTypeChange(item)}leftIcon={recordSimg}width={'100%'}style={{ width: "100%", color: 'red' }}labelKey="label"valueKey="value"/>
代码组件
import React, { useState, useRef } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Image, Modal } from 'react-native';/*** 通用下拉筛选组件(横向筛选栏专用,菜单用Modal渲染)* @param {string} label - 按钮显示文字* @param {array} options - 选项数组(字符串数组或对象数组)* @param {string|object} value - 当前选中值* @param {function} onChange - 选中项变化回调* @param {number} width - 按钮宽度(可选)* @param {string} labelKey - 对象数组中用于显示的字段名(可选,默认为'label')* @param {string} valueKey - 对象数组中用于值的字段名(可选,默认为'value')* @param {string} displayKey - 按钮显示文字的字段名(可选,默认为'label')*/
const FilterDropdown = ({label = '',options = [],value = '',onChange = () => {}, // 默认空函数width = zero.yWidth(100),leftIcon,disabled = false,style = {}, // 新增,接收外部stylelabelKey = 'label', // 对象数组中用于显示的字段名valueKey = 'value', // 对象数组中用于值的字段名displayKey = 'label', // 按钮显示文字的字段名
}) => {const [visible, setVisible] = useState(false);const btnRef = useRef();const [btnLayout, setBtnLayout] = useState({ x: 0, y: 0, width: width, height: zero.yHeight(32) });// 判断是否为对象数组const isObjectArray = options.length > 0 && typeof options[0] === 'object';// 获取显示文本const getDisplayText = (item) => {if (isObjectArray) {return item[displayKey] || item[labelKey] || '';}return item;};// 获取选项值const getOptionValue = (item) => {if (isObjectArray) {return item[valueKey] || item[labelKey] || '';}return item;};// 获取当前选中项的显示文本const getCurrentDisplayText = () => {if (!value) return label;if (isObjectArray) {const selectedOption = options.find(option => getOptionValue(option) === value);return selectedOption ? getDisplayText(selectedOption) : label;}return value;};// 记录按钮宽度和位置,菜单宽度与按钮对齐const handleBtnLayout = (e) => {if (btnRef.current) {btnRef.current.measureInWindow((x, y, width, height) => {setBtnLayout({ x, y, width, height });});}};return (<View style={[styles.wrap, style]}><TouchableOpacityref={btnRef}style={[styles.btn, value ? styles.btnActive : null, { width }]}activeOpacity={0.8}onPress={() => {if (disabled) returnhandleBtnLayout();setVisible(!visible);}}><View style={{ flexDirection: 'row', alignItems: 'center' }}>{leftIcon && (<Imagesource={leftIcon}style={[styles.leftIcon]}/>)}<Text style={[styles.btnText, value ? styles.btnTextActive : null]}>{getCurrentDisplayText()}</Text></View><Imagesource={visible ? require('../res/arrowup.png') : require('../res/arrowdown.png')}style={[styles.arrowIcon]}/></TouchableOpacity><Modalvisible={visible}transparentanimationType="fade"onRequestClose={() => setVisible(false)}><TouchableOpacitystyle={styles.modalMask}activeOpacity={1}onPress={() => setVisible(false)}><View style={[styles.dropdown,{position: 'absolute',left: btnLayout.x,top: btnLayout.y + btnLayout.height,width: btnLayout.width,}]}>{options.map((opt, idx) => {const optionValue = getOptionValue(opt);const optionText = getDisplayText(opt);const isSelected = isObjectArray ? optionValue === value : opt === value;return (<React.Fragment key={optionValue || idx}><TouchableOpacitystyle={styles.option}onPress={() => {setVisible(false);onChange && onChange(optionValue, opt);}}><Text style={[styles.optionText, isSelected && styles.optionTextActive]}>{optionText}</Text></TouchableOpacity>{idx !== options.length - 1 && <View style={styles.divider} />}</React.Fragment>);})}</View></TouchableOpacity></Modal></View>);
};const styles = StyleSheet.create({wrap: {position: 'relative',marginRight: zero.yWidth(8),justifyContent: 'space-between',zIndex: 10,// width: zero.yWidth(87), // 移除固定宽度// height: zero.yHeight(29.43), // 移除固定高度},btn: {flexDirection: 'row',alignItems: 'center',backgroundColor: '#F5F7FA',borderRadius: zero.yHeight(10),paddingHorizontal: zero.yWidth(10),height: zero.yHeight(32),borderWidth: 1,borderColor: 'transparent',minWidth: zero.yWidth(70),justifyContent: 'space-between',},btnActive: {// borderColor: '#1890FF',// backgroundColor: '#E6F0FF',},btnText: {color: '#222',fontSize: zero.yFont(15),},btnTextActive: {color: '#000000',fontWeight: 'bold',},leftIcon:{width: zero.yWidth(18),height: zero.yHeight(18),marginRight:zero.yWidth(15),resizeMode: 'contain',},arrowIcon: {width: zero.yWidth(9.19),height: zero.yHeight(5.89),marginLeft: zero.yWidth(4),tintColor: '#1890FF',resizeMode: 'contain',transform: [{ rotate: '0deg' }],},arrowIconUp: {transform: [{ rotate: '180deg' }],},arrowActive: {tintColor: '#1890FF',},modalMask: {flex: 1,backgroundColor: 'rgba(0,0,0,0.1)',},dropdown: {backgroundColor: '#fff',borderRadius: zero.yHeight(10),shadowColor: '#000',shadowOffset: { width: 0, height: 2 },shadowOpacity: 0.08,shadowRadius: zero.yHeight(8),elevation: zero.yHeight(8),minWidth: zero.yWidth(80),overflow: 'hidden',},option: {paddingVertical: zero.yHeight(12),paddingHorizontal: zero.yWidth(20),backgroundColor: '#fff',},optionText: {color: '#222',fontSize: zero.yFont(15),textAlign: 'left',},optionTextActive: {color: '#1890FF',fontWeight: 'bold',},divider: {height: zero.yHeight(1),backgroundColor: '#F0F0F0',marginHorizontal: zero.yWidth(10),},
});export default FilterDropdown;