ant design有个组件cascader组件,但是不同时支持模糊搜索和远程数据加载,那么自己实现个。
需要实现的需求
代码都是简化的,如果要使用到实际中,请自己加以改造
class CascaderSelf extends React.Component {
constructor(props) {
super(props);
this.throttleData = Throttle(this.getChildData);
this.scrollDataDeb= Debounce(this.scrollData)
}
state = {
show1: false,
parentValue: '',
parentText: '',
childNode: [],
childText: '',
childValue: '',
ParentNode: [],
loading: false,
filterValue: '',
mode: false,
page: 1,
pageSize:20,
total: 0,
};
componentDidMount() {
this.setState({
ParentNode: this.props.ParentNode,
parentText: this.props.parentText,
mode: this.props.mode
})
}
componentWillReceiveProps(props) {
const {
ParentNode,
childText,
mode = false,
} = props;
this.setState({
ParentNode,
childText,
mode,
})
}
showLabel = (e) => {
this.setState({
show1: true,
left1: e.currentTarget.getBoundingClientRect().left,
top1: e.currentTarget.getBoundingClientRect().top + e.currentTarget.clientHeight + 2,
width: e.currentTarget.clientWidth
})
}
showLabel2 = (e) => {
e.stopPropagation();
const parentValue = e.target.getAttribute('value');
const {
mode,
pageSize
} = this.state;
const params = {
value: parentValue,
};
if (mode) {
params['page'] = 1;
params['pageSize'] = pageSize;
}
setTimeout(() => {
this.getChildData({
...params,
});
}, 2)
this.setState({
parentValue,
show2: true,
parentText: e.target.innerHTML,
loading: true,
childValue: '',
childText: '',
childNode: [],
page: 1,
})
}
closeAll = (flag, e) => {
if (e.target.nodeName == 'LI') {
if (flag) {
const {
childNode
} = this.state;
const childValue = e.target.getAttribute('value');
const childText = e.target.innerHTML;
const chlidOpts = childNode.filter(v => v.value == childValue)
const {
parentValue
} = this.state;
this.setState({
childValue,
childText
})
this.props.onChange([parentValue, childValue], chlidOpts[0] || {})
}
}
if (e.target.nodeName != 'Input') {
this.setState({
show1: false,
show2: false,
loading: false,
childNode: []
})
}
}
getChildData = (params) => {
let {
page,
childNode,
} = this.state;
this.props.loadData(params, (newNode, total = 0) => {
this.setState({
childNode: [...childNode, ...newNode],
loading: false,
page: ++page,
total,
})
})
}
render(){
const {
show1,
parentValue,
show2,
left1,
top1,
width,
childNode,
childValue,
childText,
ParentNode,
loading,
filterValue,
mode
} = this.state;
const {
showLabel,
closeAll,
searchChildData,
scrollDataDeb,
} = this;
}
scrollData = () => {
let scrollTop = this.listRef.scrollTop;
let clientHeight = this.listRef.clientHeight;
let scrollHeight = this.listRef.scrollHeight;
const {
loading,
page,
mode,
parentValue,
filterValue,
pageSize,
total,
} = this.state;
if (total <= (page - 1) * pageSize) return;
if ((scrollTop + clientHeight >= scrollHeight - 20) && !loading) {
this.setState({
loading: true
});
setTimeout(() => {
this.getChildData({
value: parentValue,
name: filterValue,
page,
pageSize
});
}, 2)
}
}
searchChildData = (e) => {
const {
mode,
parentValue,
pageSize,
filterValue: oldfilterValue,
childNode
} = this.state;
const filterValue = e.target.value.trim('');
if (filterValue == oldfilterValue) return;
this.setState({
filterValue,
page: 1,
childNode: mode ? [] : childNode
});
if (!mode) return;
setTimeout(() => {
this.throttleData({
value: parentValue,
name: filterValue,
page:1,
pageSize
});
}, 2)
}
return (
{
}}
onFocus={e => {
showLabel(e)
}}
value={childText}
className={'onlyButton'}
/>
{show1 && closeAll(false, e)}>
{ParentNode.map(v => - {v.label}
)}
{show2 &&
{
e.stopPropagation()
}}
onChange={
e => {
searchChildData(e)
}
}
value={filterValue}
/>
{mode&& this.listRef = el} onScroll={e => { scrollDataDeb() }} style={{ minWidth: width + 'px', background: '#fff', height: '300px', overflow: 'auto' }} onClick={e => this.closeAll(true, e)}>
{childNode.map(v => - {v.label}
)}
}
{!mode&& this.closeAll(true, e)}>
{filterValue ?
childNode.filter(v => v.label.indexOf(filterValue) > -1).map(v => - {v.label}
) :
childNode.map(v => - {v.label}
)
}
}
}
}
);
}
上文中的节流和防抖大家自行实现,主要讲讲下面几点
下面是部分样式
.common {
background: #fff;
min-height: 30px;
line-height: 30px;
padding: 0 8px;
cursor: pointer;
&:hover {
background: #ecf6fd;
}
}
.active {
background-color: #f7f7f7;
font-weight: 600;
color: rgba(0,0,0,.65);
&:hover {
background: #f7f7f7;
}
}
.box {
background-color: #fff;
box-shadow: 0 1px 6px rgba(0,0,0,.2);
border-radius: 4px;
box-sizing: border-box;
}