背景
需要从用户选中的机构中,联动加载相对应的有权处理人。
由于有权处理人数据量较大,并且需要动态加载,无法将所有枚举都放在前端静态资源中。
需要根据所选择的机构,默认加载一部分的处理人,剩下的,根据用户手输的数据,与后台交互,模糊匹配对应的选项。
实现思路
-
使用React + hook + antd
-
需要对搜索框进行防抖处理,避免性能浪费
-
用户清除搜索条件时,应该将枚举列表恢复为上一状态。
主要代码实现
DebounceSelect:动态加载的下拉框
jsx
import React, { forwardRef, useImperativeHandle, useState, useMemo } from 'react';
import { Select, Spin } from "antd";
import debounce from "lodash/debounce";
export const DebounceSelect = forwardRef(({ fetchOptions, debounceTimeout = 800, ...props}, ref) => {
const [fetching, setFetching] = useState(false);
const [options, setOptions] = useState([]);
const [userListBackUp, setUserListBackUp] = useSate([]);
const [centerHandleOrg, setCenterHandleOrg] = useState("");
const debounceFetcher = useMemo(() => {
// keyWord:用户输入的关键字
const loadOptions = (keyWord) => {
if(!keyWord) return
setOptions([]);
setFetching(true);
// 同时触发父组件的fetchUserList,并传入参数
fetchOptions({centerHandleOrg, keyWord}).then(newOptions => {
setOptions(newOptions);
setFetching(false)
})
}
return debounce(loadOptions, debounceTimeout);
},[ centerHandleOrg, fetchOptions, debounceTimeout])
// 向父组件抛出对象
useImperativeHandle(ref, () => ({
update: (list = [], _centerHandleOrg = "") => {
setOptions(list);
setUserListBackUp(list);
setCenterHandleOrg(_centerHandleOrg);
}
}));
return(
<Select
allowClear
showSearch
filterOption={false}
onSearch={debounceFetcher}
onClear={() => setOptions(userListBackUp)}
notFoundContent={fetching ? <Spin size="small"/> : null}
{...props}
options={options}
/>
)
})
父组件中机构和处理人的部分静态代码实现:
jsx
// ...相关依赖引入
<Row>
<Col span={11}>
<Form.Item label="处理机构" name="centerHandleOrg">
<Select
allowClear
showSearch
filterOption={(val, option) => selectFilterIsBool(val, option)}
onSelect={this.centerHandleOrgChangeHandle}
onClear={() => this.debounceSelectRef.current.update()}>
{this.state.centerHandleOrgList.map(item => (
<Option key={item.orgCode} value={item.orgCode}>
{item.orgCode}-{item.orgName}
</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={11}>
<Form.Item label="处理人" name="centerHandleUser">
<DebounceSelect
ref={this.debounceSelectRef}
placeholder="请输入进行查找"
fetchOption={this.fetchUserList}
/>
</Form.Item>
</Col>
</Row>
部分关键业务逻辑代码实现
jsx
// 当用户选中某一机构时
centerHandleOrgChangeHandle = async (v) => {
// 先置空处理人栏位
this.formRef.current.setFieldsValue({
centerHandleUser: "",
})
if(!v){
return
}
// 获取有权用户列表(非全量)
const data = {centerHandleOrg: v};
const {list = []} = await getOrgUserList(data);
this.debounceSelectRef.current.update(reNameObj(list));
}
// 当用户输入关键字进行检索有权用户时
// 在子组件中会触发onSearch => 触发fetchUserList
fetchUserList = async (data) => {
return new Promise(resolve => {
const data = {centerHandleOrg: v};
const {list = []} = await getOrgUserList(data);
resolve(reNameObj(list)); // 向子中注入新的options,查询数据完成
})
}
部分使用到的方法代码实现如下:
js
// 由于select是默认按照label,value做键的
const reNameObj = (list) =>
list.map(({userName, userCode, ...rest}) => ({
label: `${userCode}-${userName},`
value: userCode,
...rest,
}))
// 前端进行模糊匹配的时候,如果返显的下拉选项为拼接后的话,会被拆成一个数组,无法直接匹配
const selectFilterIsBool = (val = "", option = "") => {
if(option.props.children){
if(Objec.prototype.toString.call(option.props.children) !== "[object Array]"){
return option.props.children.includes(val);
}
return option.props.children.join("").includes(val);
}
return false;
}
部分效果预览
搜索中:
总结:
使用防抖减少接口频繁调用
使用缓存减少性能消耗
使用数据备份这种方式提示用户体验
仅作为工作记录,感谢大家观看