mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: TreeSelect & InputTree支持menuTpl, enableDefaultIcon (#6161)
This commit is contained in:
parent
e60dcb7ba9
commit
f9930900de
@ -946,12 +946,113 @@ true false false [{label: 'A/B/C', value: 'a/b/c'},{label: 'A
|
||||
}
|
||||
```
|
||||
|
||||
## 自定义选项渲染
|
||||
|
||||
> `2.7.3` 及以上版本
|
||||
|
||||
使用`menuTpl`属性,自定义下拉选项的渲染内容。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree",
|
||||
"label": "Tree",
|
||||
"menuTpl": "<div class='flex justify-between'><span>${label}</span><span class='bg-gray-200 rounded p-1 text-xs text-center w-14'>${tag}</span></div>",
|
||||
"iconField": "icon",
|
||||
"options": [
|
||||
{
|
||||
"label": "采购单",
|
||||
"value": "order",
|
||||
"tag": "数据模型",
|
||||
"icon": "fa fa-database",
|
||||
"children": [
|
||||
{
|
||||
"label": "ID",
|
||||
"value": "id",
|
||||
"tag": "数字",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "采购人",
|
||||
"value": "name",
|
||||
"tag": "字符串",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "采购时间",
|
||||
"value": "time",
|
||||
"tag": "日期时间",
|
||||
"icon": "fa fa-check",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 选项搜索
|
||||
|
||||
> `2.7.3` 及以上版本
|
||||
|
||||
开启`"searchable": true`后,支持搜索当前数据源内的选项
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-tree",
|
||||
"name": "tree",
|
||||
"label": "Tree",
|
||||
"deferApi": "/api/mock2/form/deferOptions?label=${label}&waitSeconds=2",
|
||||
"searchable": true,
|
||||
"searchConfig": {
|
||||
"sticky": true
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"label": "Folder A",
|
||||
"value": 1,
|
||||
"collapsed": true,
|
||||
"children": [
|
||||
{
|
||||
"label": "file A",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"label": "file B",
|
||||
"value": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "这下面是懒加载的",
|
||||
"value": 4,
|
||||
"defer": true
|
||||
},
|
||||
{
|
||||
"label": "file D",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ---------------------- | -------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ---------------------- | -------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- |
|
||||
| options | `Array<object>`或`Array<string>` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) |
|
||||
| source | `string`或 [API](../../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) |
|
||||
| autoComplete | [API](../../../../docs/types/api) | | [自动提示补全](./options#%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8-autocomplete) |
|
||||
@ -970,7 +1071,7 @@ true false false [{label: 'A/B/C', value: 'a/b/c'},{label: 'A
|
||||
| editApi | [API](../../../docs/types/api) | | [配置编辑选项接口](./options#%E9%85%8D%E7%BD%AE%E7%BC%96%E8%BE%91%E6%8E%A5%E5%8F%A3-editapi) |
|
||||
| removable | `boolean` | `false` | [删除选项](./options#%E5%88%A0%E9%99%A4%E9%80%89%E9%A1%B9) |
|
||||
| deleteApi | [API](../../../docs/types/api) | | [配置删除选项接口](./options#%E9%85%8D%E7%BD%AE%E5%88%A0%E9%99%A4%E6%8E%A5%E5%8F%A3-deleteapi) |
|
||||
| searchable | `boolean` | `false` | 是否可检索,仅在 type 为 `tree-select` 的时候生效 |
|
||||
| searchable | `boolean` | `false` | 是否可检索 | `2.7.3`前仅`tree-select`支持 |
|
||||
| hideRoot | `boolean` | `true` | 如果想要显示个顶级节点,请设置为 `false` |
|
||||
| rootLabel | `boolean` | `"顶级"` | 当 `hideRoot` 不为 `false` 时有用,用来设置顶级节点的文字。 |
|
||||
| showIcon | `boolean` | `true` | 是否显示图标 |
|
||||
@ -993,6 +1094,8 @@ true false false [{label: 'A/B/C', value: 'a/b/c'},{label: 'A
|
||||
| highlightTxt | `string` | | 标签中需要高亮的字符,支持变量 |
|
||||
| itemHeight | `number` | `32` | 每个选项的高度,用于虚拟渲染 |
|
||||
| virtualThreshold | `number` | `100` | 在选项数量超过多少时开启虚拟渲染 |
|
||||
| menuTpl | `string` | | 选项自定义渲染 HTML 片段 | `2.7.3` |
|
||||
| enableDefaultIcon | `boolean` | `true` | 是否为选项添加默认的前缀 Icon,父节点默认为`folder`,叶节点默认为`file` | `2.7.3` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -322,14 +322,86 @@ order: 60
|
||||
}
|
||||
```
|
||||
|
||||
## 自定义选项渲染
|
||||
|
||||
> `2.7.3` 及以上版本
|
||||
|
||||
使用`menuTpl`属性,自定义下拉选项的渲染内容。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "tree-select",
|
||||
"name": "tree",
|
||||
"label": "Tree",
|
||||
"menuTpl": "<div class='flex justify-between'><span>${label}</span><span class='bg-gray-200 rounded p-1 text-xs text-center w-24'>${tag}</span></div>",
|
||||
"iconField": "icon",
|
||||
"searchable": true,
|
||||
"options": [
|
||||
{
|
||||
"label": "采购单",
|
||||
"value": "order",
|
||||
"tag": "数据模型",
|
||||
"icon": "fa fa-database",
|
||||
"children": [
|
||||
{
|
||||
"label": "ID",
|
||||
"value": "id",
|
||||
"tag": "数字",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "采购人",
|
||||
"value": "name",
|
||||
"tag": "字符串",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "采购时间",
|
||||
"value": "time",
|
||||
"tag": "日期时间",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "供应商",
|
||||
"value": "vendor",
|
||||
"tag": "数据模型(N:1)",
|
||||
"icon": "fa fa-database",
|
||||
"children": [
|
||||
{
|
||||
"label": "供应商ID",
|
||||
"value": "vendor_id",
|
||||
"tag": "数字",
|
||||
"icon": "fa fa-check",
|
||||
},
|
||||
{
|
||||
"label": "供应商名称",
|
||||
"value": "vendor_name",
|
||||
"tag": "字符串",
|
||||
"icon": "fa fa-check",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
更多用法,见 [InputTree](./input-tree)
|
||||
下列属性为`tree-select`独占属性, 更多属性用法,参考[InputTree 树形选择框](./input-tree)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ----------------- | --------- | ------- | ------------------------------------------- |
|
||||
| hideNodePathLabel | `boolean` | `false` | 是否隐藏选择框中已选择节点的路径 label 信息 |
|
||||
| onlyLeaf | `boolean` | `false` | 只允许选择叶子节点 |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ----------------- | --------- | ------- | ------------------------------------------------- | ---- |
|
||||
| hideNodePathLabel | `boolean` | `false` | 是否隐藏选择框中已选择节点的路径 label 信息 | |
|
||||
| onlyLeaf | `boolean` | `false` | 只允许选择叶子节点 | |
|
||||
| searchable | `boolean` | `false` | 是否可检索,仅在 type 为 `tree-select` 的时候生效 | |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
{
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"packages": ["packages/*"],
|
||||
"version": "2.7.2"
|
||||
}
|
||||
|
@ -2780,6 +2780,7 @@
|
||||
--ResultBox-value-color: var(--select-multiple-color);
|
||||
--ResultBox-value-clear-bg: var(--colors-neutral-fill-8);
|
||||
--ResultBox-value-clear-hover-bg: var(--colors-neutral-fill-9);
|
||||
--Tree-max-height: 300px;
|
||||
--Tree-indent: var(--gap-md);
|
||||
--Tree-icon-gap: var(--sizes-size-5);
|
||||
--Tree-icon-margin-right: #{px2rem(8px)};
|
||||
|
@ -2,7 +2,7 @@
|
||||
border: 1px solid var(--Form-input-borderColor);
|
||||
padding: 6px 12px;
|
||||
border-radius: 2px;
|
||||
max-height: 300px;
|
||||
max-height: var(--Tree-max-height);
|
||||
overflow: auto;
|
||||
|
||||
&.h-full {
|
||||
@ -13,6 +13,25 @@
|
||||
&.no-border {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.is-sticky {
|
||||
max-height: unset;
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
&-searchbox {
|
||||
margin-top: var(--gap-sm);
|
||||
margin-bottom: var(--gap-md);
|
||||
|
||||
&.is-active {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
& > .#{$ns}Tree {
|
||||
max-height: var(--Tree-max-height);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Tree {
|
||||
|
@ -143,6 +143,7 @@ interface TreeSelectorProps extends ThemeProps, LocaleProps, SpinnerExtraProps {
|
||||
draggable?: boolean;
|
||||
onMove?: (dropInfo: IDropInfo) => void;
|
||||
itemRender?: (option: Option, states: ItemRenderStates) => JSX.Element;
|
||||
enableDefaultIcon?: boolean;
|
||||
}
|
||||
|
||||
interface TreeSelectorState {
|
||||
@ -197,7 +198,8 @@ export class TreeSelector extends React.Component<
|
||||
pathSeparator: '/',
|
||||
nodePath: [],
|
||||
virtualThreshold: 100,
|
||||
itemHeight: 32
|
||||
itemHeight: 32,
|
||||
enableDefaultIcon: true
|
||||
};
|
||||
// 展开的节点
|
||||
unfolded: WeakMap<Object, boolean> = new WeakMap();
|
||||
@ -1046,7 +1048,8 @@ export class TreeSelector extends React.Component<
|
||||
translate: __,
|
||||
itemRender,
|
||||
draggable,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
enableDefaultIcon
|
||||
} = this.props;
|
||||
|
||||
const item = this.state.flattenedOptions[index];
|
||||
@ -1081,9 +1084,7 @@ export class TreeSelector extends React.Component<
|
||||
|
||||
const isLeaf =
|
||||
(!item.children || !item.children.length) && !item.placeholder;
|
||||
|
||||
const iconValue = item[iconField] || (item.children ? 'folder' : 'file');
|
||||
|
||||
const iconValue = item[iconField] || (enableDefaultIcon !== false ? (item.children ? 'folder' : 'file') : false);
|
||||
const level = item.level ? item.level - 1 : 0;
|
||||
|
||||
let body = null;
|
||||
@ -1153,13 +1154,15 @@ export class TreeSelector extends React.Component<
|
||||
: this.handleSelect(item))
|
||||
}
|
||||
>
|
||||
{getIcon(iconValue) ? (
|
||||
<Icon icon={iconValue} className="icon" />
|
||||
) : React.isValidElement(iconValue) ? (
|
||||
iconValue
|
||||
) : (
|
||||
<i className={iconValue}></i>
|
||||
)}
|
||||
{iconValue ? (
|
||||
getIcon(iconValue) ? (
|
||||
<Icon icon={iconValue} className="icon" />
|
||||
) : React.isValidElement(iconValue) ? (
|
||||
iconValue
|
||||
) : (
|
||||
<i className={iconValue}></i>
|
||||
)
|
||||
) : null}
|
||||
</i>
|
||||
) : null}
|
||||
|
||||
@ -1173,17 +1176,15 @@ export class TreeSelector extends React.Component<
|
||||
}
|
||||
title={item[labelField]}
|
||||
>
|
||||
{highlightTxt
|
||||
? highlight(`${item[labelField]}`, highlightTxt)
|
||||
: itemRender
|
||||
? itemRender(item, {
|
||||
index: item.key,
|
||||
multiple: multiple,
|
||||
checked: checked,
|
||||
onChange: () => this.handleCheck(item, !checked),
|
||||
disabled: disabled || item.disabled
|
||||
})
|
||||
: `${item[labelField]}`}
|
||||
{
|
||||
itemRender ? itemRender(item, {
|
||||
index: item.key,
|
||||
multiple: multiple,
|
||||
checked: checked,
|
||||
onChange: () => this.handleCheck(item, !checked),
|
||||
disabled: disabled || item.disabled
|
||||
}) : (highlightTxt ? highlight(`${item[labelField]}`, highlightTxt) : `${item[labelField]}`)
|
||||
}
|
||||
</span>
|
||||
|
||||
{!disabled &&
|
||||
|
@ -1,7 +1,11 @@
|
||||
import React from 'react';
|
||||
import omit from 'lodash/omit';
|
||||
import debounce from 'lodash/debounce';
|
||||
import cx from 'classnames';
|
||||
import {matchSorter} from 'match-sorter';
|
||||
import {SpinnerExtraProps, Tree as TreeSelector} from 'amis-ui';
|
||||
import {
|
||||
Option,
|
||||
OptionsControl,
|
||||
OptionsControlProps,
|
||||
autobind,
|
||||
@ -12,9 +16,10 @@ import {
|
||||
resolveEventData,
|
||||
toNumber
|
||||
} from 'amis-core';
|
||||
import {Spinner} from 'amis-ui';
|
||||
import {Spinner, SearchBox} from 'amis-ui';
|
||||
import {FormOptionsSchema, SchemaApi} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import type {ItemRenderStates} from 'amis-ui/lib/components/Selection';
|
||||
|
||||
/**
|
||||
* Tree 下拉选择框。
|
||||
@ -98,6 +103,55 @@ export interface TreeControlSchema extends FormOptionsSchema {
|
||||
* 需要高亮的字符串
|
||||
*/
|
||||
highlightTxt?: string;
|
||||
|
||||
/**
|
||||
* 是否为选项添加默认的Icon,默认值为true
|
||||
*/
|
||||
enableDefaultIcon?: boolean;
|
||||
|
||||
/**
|
||||
* 是否开启搜索
|
||||
*/
|
||||
searchable?: boolean;
|
||||
|
||||
/**
|
||||
* 搜索框的配置
|
||||
*/
|
||||
searchConfig?: {
|
||||
/**
|
||||
* 搜索框外层CSS样式类
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* 占位符
|
||||
*/
|
||||
placeholder?: string;
|
||||
|
||||
/**
|
||||
* 是否为 Mini 样式。
|
||||
*/
|
||||
mini?: boolean;
|
||||
|
||||
/**
|
||||
* 是否为加强样式
|
||||
*/
|
||||
enhance?: boolean;
|
||||
|
||||
/**
|
||||
* 是否可清除
|
||||
*/
|
||||
clearable?: boolean;
|
||||
|
||||
/**
|
||||
* 是否立马搜索。
|
||||
*/
|
||||
searchImediately?: boolean;
|
||||
|
||||
/**
|
||||
* 搜索框是否吸顶
|
||||
*/
|
||||
sticky?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TreeProps
|
||||
@ -116,9 +170,14 @@ export interface TreeProps
|
||||
pathSeparator?: string;
|
||||
}
|
||||
|
||||
export default class TreeControl extends React.Component<TreeProps> {
|
||||
interface TreeState {
|
||||
filteredOptions: Option[];
|
||||
keyword: string;
|
||||
}
|
||||
|
||||
export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
static defaultProps: Partial<TreeProps> = {
|
||||
placeholder: 'loading',
|
||||
placeholder: 'placeholder.noData',
|
||||
multiple: false,
|
||||
rootLabel: 'Tree.root',
|
||||
rootValue: '',
|
||||
@ -128,6 +187,35 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
};
|
||||
treeRef: any;
|
||||
|
||||
constructor(props: TreeProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
keyword: '',
|
||||
filteredOptions: this.props.options ?? []
|
||||
};
|
||||
this.handleSearch = debounce(this.handleSearch.bind(this), 250, {
|
||||
trailing: true,
|
||||
leading: false
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TreeProps) {
|
||||
const props = this.props;
|
||||
const keyword = this.state.keyword;
|
||||
|
||||
if (
|
||||
prevProps.options !== props.options ||
|
||||
prevProps.searchable !== props.searchable
|
||||
) {
|
||||
const {options, searchable} = props;
|
||||
|
||||
this.setState({
|
||||
filteredOptions:
|
||||
searchable && keyword ? this.filterOptions(options, keyword) : options
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
reload() {
|
||||
const reload = this.props.reloadOptions;
|
||||
reload && reload();
|
||||
@ -148,6 +236,30 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
}
|
||||
}
|
||||
|
||||
filterOptions(options: Array<Option>, keywords: string): Array<Option> {
|
||||
const {labelField, valueField} = this.props;
|
||||
|
||||
return options.map(option => {
|
||||
option = {
|
||||
...option
|
||||
};
|
||||
option.visible = !!matchSorter([option], keywords, {
|
||||
keys: [labelField || 'label', valueField || 'value']
|
||||
}).length;
|
||||
|
||||
if (!option.visible && option.children) {
|
||||
option.children = this.filterOptions(option.children, keywords);
|
||||
const visibleCount = option.children.filter(
|
||||
item => item.visible
|
||||
).length;
|
||||
option.visible = !!visibleCount;
|
||||
}
|
||||
|
||||
option.visible && (option.collapsed = false);
|
||||
return option;
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
async handleChange(value: any) {
|
||||
const {onChange, dispatchEvent} = this.props;
|
||||
@ -164,6 +276,16 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
onChange && onChange(value);
|
||||
}
|
||||
|
||||
handleSearch(keyword: string) {
|
||||
const {options} = this.props;
|
||||
const filterOptions = this.filterOptions(options, keyword);
|
||||
|
||||
this.setState({
|
||||
keyword,
|
||||
filteredOptions: keyword ? filterOptions : options
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
domRef(ref: any) {
|
||||
this.treeRef = ref;
|
||||
@ -182,6 +304,15 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderOptionItem(option: Option, states: ItemRenderStates) {
|
||||
const {menuTpl, render, data} = this.props;
|
||||
|
||||
return render(`option/${states.index}`, menuTpl, {
|
||||
data: createObject(createObject(data, {...states}), option)
|
||||
});
|
||||
}
|
||||
|
||||
@supportStatic()
|
||||
render() {
|
||||
const {
|
||||
@ -236,17 +367,80 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
data,
|
||||
virtualThreshold,
|
||||
itemHeight,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
menuTpl,
|
||||
enableDefaultIcon,
|
||||
searchable,
|
||||
searchConfig = {}
|
||||
} = this.props;
|
||||
let {highlightTxt} = this.props;
|
||||
const {filteredOptions, keyword} = this.state;
|
||||
|
||||
if (isPureVariable(highlightTxt)) {
|
||||
highlightTxt = resolveVariableAndFilter(highlightTxt, data);
|
||||
}
|
||||
|
||||
const TreeCmpt = (
|
||||
<TreeSelector
|
||||
classPrefix={ns}
|
||||
onRef={this.domRef}
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
iconField={iconField}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
joinValues={joinValues}
|
||||
extractValue={extractValue}
|
||||
delimiter={delimiter}
|
||||
placeholder={__(placeholder)}
|
||||
options={searchable ? filteredOptions : options}
|
||||
highlightTxt={searchable ? keyword : highlightTxt}
|
||||
multiple={multiple}
|
||||
initiallyOpen={initiallyOpen}
|
||||
unfoldedLevel={unfoldedLevel}
|
||||
withChildren={withChildren}
|
||||
onlyChildren={onlyChildren}
|
||||
onlyLeaf={onlyLeaf}
|
||||
hideRoot={hideRoot}
|
||||
rootLabel={__(rootLabel)}
|
||||
rootValue={rootValue}
|
||||
showIcon={showIcon}
|
||||
showRadio={showRadio}
|
||||
showOutline={showOutline}
|
||||
autoCheckChildren={autoCheckChildren}
|
||||
cascade={cascade}
|
||||
foldedField="collapsed"
|
||||
value={value || ''}
|
||||
nodePath={nodePath}
|
||||
enableNodePath={enableNodePath}
|
||||
pathSeparator={pathSeparator}
|
||||
selfDisabledAffectChildren={false}
|
||||
onAdd={onAdd}
|
||||
creatable={creatable}
|
||||
createTip={createTip}
|
||||
rootCreatable={rootCreatable}
|
||||
rootCreateTip={rootCreateTip}
|
||||
onEdit={onEdit}
|
||||
editable={editable}
|
||||
editTip={editTip}
|
||||
removable={removable}
|
||||
removeTip={removeTip}
|
||||
onDelete={onDelete}
|
||||
bultinCUD={!addControls && !editControls}
|
||||
onDeferLoad={deferLoad}
|
||||
onExpandTree={expandTreeOptions}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined}
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`${ns}TreeControl`, className, treeContainerClassName)}
|
||||
className={cx(`${ns}TreeControl`, className, treeContainerClassName, {
|
||||
'is-sticky': searchable && searchConfig?.sticky
|
||||
})}
|
||||
>
|
||||
<Spinner
|
||||
size="sm"
|
||||
@ -254,60 +448,23 @@ export default class TreeControl extends React.Component<TreeProps> {
|
||||
show={loading}
|
||||
loadingConfig={loadingConfig}
|
||||
/>
|
||||
{loading ? null : (
|
||||
<TreeSelector
|
||||
classPrefix={ns}
|
||||
onRef={this.domRef}
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
iconField={iconField}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
joinValues={joinValues}
|
||||
extractValue={extractValue}
|
||||
delimiter={delimiter}
|
||||
placeholder={__(placeholder)}
|
||||
options={options}
|
||||
highlightTxt={highlightTxt}
|
||||
multiple={multiple}
|
||||
initiallyOpen={initiallyOpen}
|
||||
unfoldedLevel={unfoldedLevel}
|
||||
withChildren={withChildren}
|
||||
onlyChildren={onlyChildren}
|
||||
onlyLeaf={onlyLeaf}
|
||||
hideRoot={hideRoot}
|
||||
rootLabel={__(rootLabel)}
|
||||
rootValue={rootValue}
|
||||
showIcon={showIcon}
|
||||
showRadio={showRadio}
|
||||
showOutline={showOutline}
|
||||
autoCheckChildren={autoCheckChildren}
|
||||
cascade={cascade}
|
||||
foldedField="collapsed"
|
||||
value={value || ''}
|
||||
nodePath={nodePath}
|
||||
enableNodePath={enableNodePath}
|
||||
pathSeparator={pathSeparator}
|
||||
selfDisabledAffectChildren={false}
|
||||
onAdd={onAdd}
|
||||
creatable={creatable}
|
||||
createTip={createTip}
|
||||
rootCreatable={rootCreatable}
|
||||
rootCreateTip={rootCreateTip}
|
||||
onEdit={onEdit}
|
||||
editable={editable}
|
||||
editTip={editTip}
|
||||
removable={removable}
|
||||
removeTip={removeTip}
|
||||
onDelete={onDelete}
|
||||
bultinCUD={!addControls && !editControls}
|
||||
onDeferLoad={deferLoad}
|
||||
onExpandTree={expandTreeOptions}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={
|
||||
toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined
|
||||
}
|
||||
/>
|
||||
{loading ? null : searchable ? (
|
||||
<>
|
||||
<SearchBox
|
||||
className={cx(
|
||||
`${ns}TreeControl-searchbox`,
|
||||
searchConfig?.className,
|
||||
{'is-sticky': searchConfig?.sticky}
|
||||
)}
|
||||
mini={false}
|
||||
clearable={true}
|
||||
{...omit(searchConfig, 'className', 'sticky')}
|
||||
onSearch={this.handleSearch}
|
||||
/>
|
||||
{TreeCmpt}
|
||||
</>
|
||||
) : (
|
||||
TreeCmpt
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -26,6 +26,7 @@ import {ActionObject} from 'amis-core';
|
||||
import {FormOptionsSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import {TooltipWrapperSchema} from '../TooltipWrapper';
|
||||
import type {ItemRenderStates} from 'amis-ui/lib/components/Selection';
|
||||
|
||||
/**
|
||||
* Tree 下拉选择框。
|
||||
@ -108,6 +109,16 @@ export interface TreeSelectControlSchema extends FormOptionsSchema {
|
||||
* 收纳标签的Popover配置
|
||||
*/
|
||||
overflowTagPopover?: TooltipWrapperSchema;
|
||||
|
||||
/**
|
||||
* 自定义选项
|
||||
*/
|
||||
menuTpl?: string;
|
||||
|
||||
/**
|
||||
* 是否为选项添加默认的Icon,默认值为true
|
||||
*/
|
||||
enableDefaultIcon?: boolean;
|
||||
}
|
||||
|
||||
export interface TreeSelectProps
|
||||
@ -484,6 +495,17 @@ export default class TreeSelectControl extends React.Component<
|
||||
onChange && onChange(value);
|
||||
}
|
||||
|
||||
/** 下拉框选项渲染 */
|
||||
@autobind
|
||||
renderOptionItem(option: Option, states: ItemRenderStates) {
|
||||
const {menuTpl, render, data} = this.props;
|
||||
|
||||
return render(`option/${states.index}`, menuTpl, {
|
||||
data: createObject(createObject(data, {...states}), option)
|
||||
});
|
||||
}
|
||||
|
||||
/** 输入框选项渲染 */
|
||||
@autobind
|
||||
renderItem(item: Option) {
|
||||
const {labelField, options, hideNodePathLabel} = this.props;
|
||||
@ -559,7 +581,9 @@ export default class TreeSelectControl extends React.Component<
|
||||
autoCheckChildren,
|
||||
hideRoot,
|
||||
virtualThreshold,
|
||||
itemHeight
|
||||
itemHeight,
|
||||
menuTpl,
|
||||
enableDefaultIcon
|
||||
} = this.props;
|
||||
|
||||
let filtedOptions =
|
||||
@ -619,6 +643,8 @@ export default class TreeSelectControl extends React.Component<
|
||||
selfDisabledAffectChildren={selfDisabledAffectChildren}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined}
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user