fix: Tree组件同时配置source & autoComplete时数据覆盖问题 (#6113)

This commit is contained in:
RUNZE LU 2023-02-08 12:06:55 +08:00 committed by GitHub
parent 31063fe68e
commit 7c2fcdfb6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 225 additions and 4 deletions

View File

@ -300,6 +300,28 @@ order: 60
}
```
## 搜索选项
> `2.7.1` 及以上版本
配置`autoComplete`接口可以实现从远程数据搜索目标结果,搜索的关键字段为`term`,注意搜索的逻辑需要在服务端实现。
```schema: scope="body"
{
"type":"form",
"api":"/api/mock2/form/saveForm",
"body":[
{
"type":"tree-select",
"name":"tree",
"label":"Tree",
"autoComplete":"/api/mock2/tree/search?term=$term",
"source":"/api/mock2/tree/search"
}
]
}
```
## 属性表
更多用法,见 [InputTree](./input-tree)

View File

@ -0,0 +1,183 @@
/**
* @file tree/autoComplete
* @desc 树形结构的自动搜索
*/
const treeOptions = [
{
label: 'node-0',
value: 'node-0',
children: [
{
label: 'node-0-0',
value: 'node-0-0',
children: [
{
label: 'node-0-0-0',
value: 'node-0-0-0',
children: [
{
label: 'node-0-0-0-0',
value: 'node-0-0-0-0'
},
{
label: 'node-0-0-0-1',
value: 'node-0-0-0-1'
},
{
label: 'node-0-0-0-2',
value: 'node-0-0-0-2'
}
]
},
{
label: 'node-0-0-1',
value: 'node-0-0-1',
children: [
{
label: 'node-0-0-1-0',
value: 'node-0-0-1-0'
}
]
},
{
label: 'node-0-0-2',
value: 'node-0-0-2',
children: [
{
label: 'node-0-0-2-0',
value: 'node-0-0-2-0'
},
{
label: 'node-0-0-2-1',
value: 'node-0-0-2-1'
},
{
label: 'node-0-0-2-2',
value: 'node-0-0-2-2'
},
{
label: 'node-0-0-2-3',
value: 'node-0-0-2-3'
}
]
}
]
},
{
label: 'node-0-1',
value: 'node-0-1',
children: [
{
label: 'node-0-1-0',
value: 'node-0-1-0',
children: [
{
label: 'node-0-1-0-0',
value: 'node-0-1-0-0'
},
{
label: 'node-0-1-0-1',
value: 'node-0-1-0-1'
}
]
},
{
label: 'node-0-1-1',
value: 'node-0-1-1',
children: [
{
label: 'node-0-1-1-0',
value: 'node-0-1-1-0'
},
{
label: 'node-0-1-1-1',
value: 'node-0-1-1-1'
},
{
label: 'node-0-1-1-2',
value: 'node-0-1-1-2'
}
]
}
]
}
]
},
{
label: 'node-1',
value: 'node-1',
children: [
{
label: 'node-1-0',
value: 'node-1-0'
}
]
},
{
label: 'node-2',
value: 'node-2'
},
{
label: 'node-3',
value: 'node-3',
children: [
{
label: 'node-3-0',
value: 'node-3-0',
children: [
{
label: 'node-3-0-0',
value: 'node-3-0-0'
},
{
label: 'node-3-0-1',
value: 'node-3-0-1'
},
{
label: 'node-3-0-2',
value: 'node-3-0-2'
},
{
label: 'node-3-0-3',
value: 'node-3-0-3'
}
]
}
]
}
];
function searchNode(keyword) {
const search = data => {
const matched = [];
data.forEach(node => {
if (node.value && ~node.value.indexOf(keyword)) {
matched.push({...node});
} else if (node.children) {
const filtered = search(node.children);
if (filtered.length) {
matched.push({...node, children: filtered});
}
}
});
return matched;
};
return search(treeOptions);
}
module.exports = function (req, res) {
const term = req.query.term || '';
res.json({
status: 0,
msg: '',
data: {
options: term ? searchNode(term) : treeOptions
}
});
};

View File

@ -163,6 +163,9 @@ export default class TreeSelectControl extends React.Component<
targetRef = (ref: any) =>
(this.target = ref ? (findDOMNode(ref) as HTMLElement) : null);
/** source数据源是否已加载 */
sourceLoaded: boolean = false;
constructor(props: TreeSelectProps) {
super(props);
@ -183,7 +186,6 @@ export default class TreeSelectControl extends React.Component<
leading: false
});
this.handleInputKeyDown = this.handleInputKeyDown.bind(this);
this.loadRemote = debouce(this.loadRemote.bind(this), 250, {
trailing: true,
leading: false
@ -194,6 +196,10 @@ export default class TreeSelectControl extends React.Component<
this.loadRemote('');
}
componentWillUnmount() {
this.sourceLoaded = false;
}
open(fn?: () => void) {
if (this.props.disabled) {
return;
@ -228,7 +234,11 @@ export default class TreeSelectControl extends React.Component<
}
handleKeyPress(e: React.KeyboardEvent) {
if (e.key === ' ') {
/**
* label/value中有空格的case
* 使 winshift + spacemacshift + space
*/
if (e.key === ' ' && e.shiftKey) {
this.handleOutClick(e as any);
e.preventDefault();
}
@ -353,9 +363,15 @@ export default class TreeSelectControl extends React.Component<
}
async loadRemote(input: string) {
const {autoComplete, env, data, setOptions, setLoading} = this.props;
const {autoComplete, env, data, setOptions, setLoading, source} =
this.props;
if (!isEffectiveApi(autoComplete, data)) {
// 同时配置source和autoComplete时首次渲染需要加载source数据
if (
!isEffectiveApi(autoComplete, data) ||
(!input && isEffectiveApi(source) && !this.sourceLoaded)
) {
this.sourceLoaded = true;
return;
} else if (!env || !env.fetcher) {
throw new Error('fetcher is required');