mh-rn-component
Version:
220 lines (219 loc) • 6.92 kB
Flow
import React, { useRef, useEffect, useState } from 'react'
import { View, Text, StyleSheet, FlatList, TouchableWithoutFeedback } from "react-native"
import Columns from "./Columns"
import Overlay from '../Overlay'
import { multiColumnData, cascadeData } from "./data"
import { CustomFieldNameType, PickerColumn, Numeric } from "./types"
type Props = {
show?: boolean,
columns: Array<PickerColumn>,//* 数据 特定数据格式或自定义数据格式
title?: string,//*顶部栏标题
confirmButtonText?: string,//*确认按钮文字
cancelButtonText?: string,//*取消按钮文字
visibleItemCount?: number,//*可见选项个数 默认5
columnHeight?: number,//*选项高度 默认44
cascadeLevel?: number,//*启用级联选择 需要填写具体几层 减少代码量 优化点
customFieldName?: CustomFieldNameType,//*自定义 columns 结构中的字段
toolbar?: React.ReactElement<{}>//todo 自定义整个顶部栏内容
showChange: (value: boolean) => void
onConfirm?: (currentValue: Numeric | Numeric[]) => void
onCancel?: (currentValue?: Numeric | Numeric[]) => void
onChange?: (currentValue: Numeric | Numeric[]) => void
}
const Picker = (
{
show,
title = "标题",
confirmButtonText = "确认",
cancelButtonText = "取消",
visibleItemCount = 5,
columnHeight = 44,
columns,
cascadeLevel,
customFieldName = {
value: "value",
children: "children"
},
toolbar,
...rest }: Props) => {
const customFieldNameValue = customFieldName.value || "value"
const customFieldNameChildren = customFieldName.children || "children"
// 当前列的选项数组
const [columnsData, setColumnsData] = useState<any[]>([])
// 要返回的选中数组
const [currentValue, setCurrentValue] = useState<Numeric[]>(new Array(columnsData.length))
// 当前各列选中的index
const [columnsSelect, setColumnsSelect] = useState(new Array(columnsData.length))
const formattedColumns = () => {
// 判断是否事级联选择
if (cascadeLevel) {
let cascadeArr = new Array(cascadeLevel)
for (let i = 0; i < cascadeLevel; i++) {
if (i === 0) {
cascadeArr[0] = { values: columns }
} else {
cascadeArr[i] = { values: cascadeArr[i - 1]?.values[0][customFieldNameChildren], defaultIndex: 0 }
}
}
setColumnsData(() => {
return [...cascadeArr]
})
} else {
setColumnsData([...columns])
}
}
// 重组 级联数据结构
const cascadeFormat = (value: number, columnsIndex: number) => {
for (let i = columnsIndex + 1; i < columnsData.length; i++) {
if (i === columnsIndex + 1) {
columnsData[i].values = columnsData[i - 1].values[value].children
} else {
columnsData[i].values = columnsData[i - 1].values[0].children
}
setColumnsSelect((prev) => {
prev[i] = 0
return [...prev]
})
setCurrentValue((prev) => {
prev[i] = columnsData[i].values[0][customFieldNameValue]
return prev
})
}
setColumnsData([...columnsData])
}
useEffect(() => {
formattedColumns()
}, [columns, cascadeLevel])
/**
* 多列选择/级联选择
* @param value 选中的要返回的数据
* @param index 选中的位置
* @param columnsIndex 改变的是第几列 0开始
*/
const multiColumnChange = (value: Numeric, index: number, columnsIndex: number) => {
setCurrentValue((prev) => {
prev[columnsIndex] = value
return prev
})
setColumnsSelect((prev) => {
prev[columnsIndex] = index
return [...prev]
})
if (cascadeLevel) {
cascadeFormat(index, columnsIndex)
}
}
const confirm = () => {
rest.onConfirm && rest.onConfirm(currentValue)
rest.showChange(!show)
}
const cancel = () => {
rest.onCancel && rest.onCancel(currentValue)
rest.showChange(!show)
}
const change = () => {
// 组合一下change
const backValue: any[] = columnsSelect.reduce((acc, cur, index) => {
let _obj = {
index: cur,
value: currentValue[index]
}
acc.push(_obj)
return acc
}, [])
if (backValue.length > 0) {
rest.onChange && rest.onChange(backValue)
}
}
useEffect(() => {
change()
}, [columnsSelect])
return (
<>
{
show && (<View style={styles.bg}>
<TouchableWithoutFeedback onPress={() => rest.showChange(false)} ><View style={styles.handel_bg}></View></TouchableWithoutFeedback>
<View style={styles.picker}>
<View style={styles.picker_title}>
<Text onPress={cancel} style={[styles.picker_fontSize, { color: "#cccccc" }]}>{cancelButtonText}</Text>
<Text style={[styles.picker_title_content]}>{title}</Text>
<Text onPress={confirm} style={[styles.picker_fontSize, { color: "#3170a7" }]}>{confirmButtonText}</Text>
</View>
<View style={[styles.picker_main, { height: columnHeight * visibleItemCount }]}>
{
columnsData.map((item: any, index: number) => {
return (
<Columns
onChange={multiColumnChange}
columnIndex={index}
visibleItemCount={visibleItemCount}
defaultIndex={item.defaultIndex || 0}
columnHeight={columnHeight}
data={item.values}
key={index}
customFieldName={customFieldName}
cascadeLevel={cascadeLevel}
></Columns>
)
})
}
{/* <View pointerEvents="none" style={[styles.picker_frame, { height: columnHeight, marginTop: -(columnHeight / 2) }]}>
</View> */}
</View>
</View>
</View>)
}
</>
)
}
const styles = StyleSheet.create({
bg: {
width: "100%",
height: "100%",
flex: 1,
justifyContent: "flex-end",
position: 'absolute',
top: 0,
left: 0,
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, .4)',
},
handel_bg: {
flex: 1,
width: '100%',
},
picker: {
width: "100%",
minHeight: 300,
// maxHeight: 440,
backgroundColor: "#ffffff",
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
picker_title: {
padding: 8,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
picker_title_content: {
fontSize: 18,
},
picker_fontSize: {
fontSize: 16,
},
picker_main: {
width: "100%",
flexDirection: "row",
alignItems: "flex-start",
overflow: "hidden",
},
picker_frame: {
width: "100%",
backgroundColor: "rgba(248,223,112,0.5)",
position: "absolute",
top: "50%",
left: 0,
}
})
export default Picker