mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
parent
eaa62a253e
commit
641f35ae37
@ -1405,21 +1405,39 @@ order: 2
|
||||
|
||||
> 1.10.0 及以上版本
|
||||
|
||||
下拉框在数据量较大时(超过 200,可以通过 `virtualThreshold` 控制)会自动切换到虚拟渲染模式,如果选项的内容较长会导致内容重叠,这时需要设置 `itemHeight` 来避免。
|
||||
下拉框在数据量较大时(超过 100,可以通过 `virtualThreshold` 控制)会自动切换到虚拟渲染模式,如果选项的内容较长会导致内容重叠,这时需要设置 `itemHeight` 来避免。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"type": "select",
|
||||
"label": "虚拟列表选择",
|
||||
"name": "virtual-select",
|
||||
"clearable": true,
|
||||
"searchable": true,
|
||||
"source": "/api/mock2/form/getOptions?waitSeconds=1&size=200"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ---------------- | --------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------- |
|
||||
| options | `Array<object>`或`Array<string>` | | 选项组,供用户选择 |
|
||||
| source | [API](../../../docs/types/api) 或 [数据映射](../../../docs/concepts/data-mapping) | | 选项组源,可通过数据映射获取当前数据域变量、或者配置 API 对象 |
|
||||
| multiple | `boolean` | `false` | 是否支持多选 |
|
||||
| labelField | `boolean` | `"label"` | 标识选项中哪个字段是`label`值 |
|
||||
| valueField | `boolean` | `"value"` | 标识选项中哪个字段是`value`值 |
|
||||
| joinValues | `boolean` | `true` | 是否拼接`value`值 |
|
||||
| extractValue | `boolean` | `false` | 是否将`value`值抽取出来组成新的数组,只有在`joinValues`是`false`是生效 |
|
||||
| itemHeight | `number` | | 每个选项的高度,用于虚拟渲染 |
|
||||
| virtualThreshold | `number` | | 在选项数量超过多少时开启虚拟渲染 |
|
||||
| valuesNoWrap | `boolean` | `false` | 默认情况下多选所有选项都会显示,通过这个可以最多显示一行,超出的部分变成 ... |
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ---------------- | --------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------- |
|
||||
| options | `Array<object>`或`Array<string>` | | 选项组,供用户选择 |
|
||||
| source | [API](../../../docs/types/api) 或 [数据映射](../../../docs/concepts/data-mapping) | | 选项组源,可通过数据映射获取当前数据域变量、或者配置 API 对象 |
|
||||
| multiple | `boolean` | `false` | 是否支持多选 |
|
||||
| labelField | `boolean` | `"label"` | 标识选项中哪个字段是`label`值 |
|
||||
| valueField | `boolean` | `"value"` | 标识选项中哪个字段是`value`值 |
|
||||
| joinValues | `boolean` | `true` | 是否拼接`value`值 |
|
||||
| extractValue | `boolean` | `false` | 是否将`value`值抽取出来组成新的数组,只有在`joinValues`是`false`是生效 |
|
||||
| itemHeight | `number` | `32` | 每个选项的高度,用于虚拟渲染 |
|
||||
| virtualThreshold | `number` | `100` | 在选项数量超过多少时开启虚拟渲染 |
|
||||
| valuesNoWrap | `boolean` | `false` | 默认情况下多选所有选项都会显示,通过这个可以最多显示一行,超出的部分变成 ... |
|
||||
|
42
mock/cfc/mock/form/getOptions.js
Normal file
42
mock/cfc/mock/form/getOptions.js
Normal file
@ -0,0 +1,42 @@
|
||||
module.exports = function (req, res) {
|
||||
const size =
|
||||
req.query.size && typeof Number(req.query.size) === 'number'
|
||||
? Math.ceil(req.query.size)
|
||||
: 0;
|
||||
|
||||
const customOptions =
|
||||
size > 0
|
||||
? Array.from({length: size}, (item, index) => ({
|
||||
label: 'Option ' + index,
|
||||
value: index.toString()
|
||||
}))
|
||||
: [];
|
||||
|
||||
const defaultOptions = [
|
||||
{label: 'Option A', value: 'a'},
|
||||
{label: 'Option B', value: 'b'},
|
||||
{label: 'Option C', value: 'c'},
|
||||
{label: 'Option D', value: 'd'},
|
||||
{label: 'Option E', value: 'e'},
|
||||
{label: 'Option F', value: 'f'},
|
||||
{label: 'Option G', value: 'g'},
|
||||
{label: 'Option H', value: 'h'},
|
||||
{label: 'Option I', value: 'i'},
|
||||
{label: 'Option J', value: 'j'},
|
||||
{label: 'Option K', value: 'k'},
|
||||
{label: 'Option L', value: 'l'},
|
||||
{label: 'Option M', value: 'm'},
|
||||
{label: 'Option N', value: 'n'},
|
||||
{label: 'Option O', value: 'o'},
|
||||
{label: 'Option P', value: 'p'},
|
||||
{label: 'Option Q', value: 'q'}
|
||||
];
|
||||
|
||||
res.json({
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
options: customOptions.length > 0 ? customOptions : defaultOptions
|
||||
}
|
||||
});
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"status": 0,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"options": [
|
||||
{"label": "Option A", "value": "a"},
|
||||
{"label": "Option B", "value": "b"},
|
||||
{"label": "Option C", "value": "c"},
|
||||
{"label": "Option D", "value": "d"},
|
||||
{"label": "Option E", "value": "e"},
|
||||
{"label": "Option F", "value": "f"},
|
||||
{"label": "Option G", "value": "g"},
|
||||
{"label": "Option H", "value": "h"},
|
||||
{"label": "Option I", "value": "i"},
|
||||
{"label": "Option J", "value": "j"},
|
||||
{"label": "Option K", "value": "k"},
|
||||
{"label": "Option L", "value": "l"},
|
||||
{"label": "Option M", "value": "m"},
|
||||
{"label": "Option N", "value": "n"},
|
||||
{"label": "Option O", "value": "o"},
|
||||
{"label": "Option P", "value": "p"},
|
||||
{"label": "Option Q", "value": "q"}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import React = require('react');
|
||||
import {NotFound} from 'amis-ui';
|
||||
import NotFound from '../src/components/404';
|
||||
import * as renderer from 'react-test-renderer';
|
||||
import {render, fireEvent, cleanup} from '@testing-library/react';
|
||||
|
||||
|
@ -81,6 +81,8 @@
|
||||
|
||||
.#{$ns}PopOver.#{$ns}Select-popover {
|
||||
.#{$ns}Select-menu {
|
||||
overflow-x: hidden;
|
||||
|
||||
.#{$ns}Select-option {
|
||||
height: px2rem(32px);
|
||||
line-height: px2rem(22px);
|
||||
|
@ -9,6 +9,7 @@ import {uncontrollable} from 'amis-core';
|
||||
import React from 'react';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
import omit from 'lodash/omit';
|
||||
import merge from 'lodash/merge';
|
||||
import VirtualList from './virtual-list';
|
||||
import Overlay from './Overlay';
|
||||
import PopOver from './PopOver';
|
||||
@ -413,7 +414,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
inputValue: '',
|
||||
highlightedIndex: -1,
|
||||
selection: value2array(props.value, props),
|
||||
itemHeight: 35,
|
||||
itemHeight: 32 /** Select选项高度保持一致 */,
|
||||
pickerSelectItem: ''
|
||||
};
|
||||
}
|
||||
@ -728,7 +729,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
|
||||
@autobind
|
||||
menuItemRef(ref: any) {
|
||||
ref && this.setState({itemHeight: ref.offsetHeight});
|
||||
if (ref && typeof ref.offsetHeight === 'number' && ref > 0) {
|
||||
this.setState({itemHeight: ref.offsetHeight});
|
||||
}
|
||||
}
|
||||
|
||||
renderValue({inputValue, isOpen}: ControllerStateAndHelpers<any>) {
|
||||
@ -946,7 +949,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
hideSelected,
|
||||
renderMenu,
|
||||
mobileClassName,
|
||||
virtualThreshold = 200,
|
||||
virtualThreshold = 100,
|
||||
useMobileUI = false
|
||||
} = this.props;
|
||||
const {selection} = this.state;
|
||||
@ -960,7 +963,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
})
|
||||
: options.concat()
|
||||
).filter((option: Option) => !option.hidden && option.visible !== false);
|
||||
|
||||
const enableVirtualRender =
|
||||
filtedOptions.length && filtedOptions.length > virtualThreshold;
|
||||
const selectionValues = selection.map(select => select[valueField]);
|
||||
if (multiple && checkAll) {
|
||||
const optionsValues = (checkAllBySearch ? filtedOptions : options).map(
|
||||
@ -1000,7 +1004,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
item,
|
||||
disabled: item.disabled
|
||||
})}
|
||||
style={style}
|
||||
style={merge(style, enableVirtualRender ? {width: '100%'} : {})}
|
||||
className={cx(`Select-option`, {
|
||||
'is-disabled': item.disabled,
|
||||
'is-highlight': highlightedIndex === index,
|
||||
@ -1107,8 +1111,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
<div
|
||||
ref={this.menu}
|
||||
className={cx('Select-menu', {
|
||||
'Select--longlist':
|
||||
filtedOptions.length && filtedOptions.length > virtualThreshold,
|
||||
'Select--longlist': enableVirtualRender,
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
>
|
||||
|
@ -1654,7 +1654,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
</span>
|
||||
<div
|
||||
class="cxd-PopOver cxd-Select-popover cxd-PopOver--leftBottomLeftTop"
|
||||
style="display: block; width: 0px; left: 0px; top: 0px; position: relative;"
|
||||
style="display: block; width: auto; left: 0px; top: 0px; position: relative;"
|
||||
theme="cxd"
|
||||
>
|
||||
<div
|
||||
@ -1674,14 +1674,14 @@ exports[`Renderer:select virtual 1`] = `
|
||||
style="overflow: auto; will-change: transform; height: 266px; width: 100%;"
|
||||
>
|
||||
<div
|
||||
style="position: relative; width: auto; white-space: nowrap; min-height: 100%; height: 7000px;"
|
||||
style="position: relative; width: auto; white-space: nowrap; min-height: 100%; height: 6400px;"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-0"
|
||||
role="option"
|
||||
style="position: absolute; top: 0px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1695,7 +1695,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-1"
|
||||
role="option"
|
||||
style="position: absolute; top: 35px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 32px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1709,7 +1709,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-2"
|
||||
role="option"
|
||||
style="position: absolute; top: 70px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 64px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1723,7 +1723,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-3"
|
||||
role="option"
|
||||
style="position: absolute; top: 105px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 96px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1737,7 +1737,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-4"
|
||||
role="option"
|
||||
style="position: absolute; top: 140px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 128px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1751,7 +1751,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-5"
|
||||
role="option"
|
||||
style="position: absolute; top: 175px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 160px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1765,7 +1765,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-6"
|
||||
role="option"
|
||||
style="position: absolute; top: 210px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 192px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1779,7 +1779,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-7"
|
||||
role="option"
|
||||
style="position: absolute; top: 245px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 224px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1793,7 +1793,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-8"
|
||||
role="option"
|
||||
style="position: absolute; top: 280px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 256px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1807,7 +1807,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-9"
|
||||
role="option"
|
||||
style="position: absolute; top: 315px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 288px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1821,7 +1821,7 @@ exports[`Renderer:select virtual 1`] = `
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-10"
|
||||
role="option"
|
||||
style="position: absolute; top: 350px; left: 0px; width: auto; height: 35px;"
|
||||
style="position: absolute; top: 320px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
@ -1830,6 +1830,20 @@ exports[`Renderer:select virtual 1`] = `
|
||||
option10
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="cxd-Select-option"
|
||||
id="downshift-1-item-11"
|
||||
role="option"
|
||||
style="position: absolute; top: 352px; left: 0px; width: 100%; height: 32px;"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-option-content"
|
||||
title="option11"
|
||||
>
|
||||
option11
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user