index.md 32 KB


localeCode: zh-CN order: 33 category: 输入类 title: Transfer 穿梭框 icon: doc-transfer width: 60% dir: column

brief: 一个更直观高效的多选选择器,可以露出更多选项的信息,支持搜索功能,缺点是占据更多空间

代码演示

如何引入

import { Transfer } from '@douyinfe/semi-ui';

基本使用

数据项需传入 value、label、key

import React from 'react';
import { Transfer } from '@douyinfe/semi-ui';

() => {
    const data = Array.from({ length: 100 }, (v, i) => {
        return {
            label: `选项名称 ${i}`,
            value: i,
            disabled: false,
            key: i,
        };
    });
    return (
        <Transfer
            style={{ width: 568, height: 416 }}
            dataSource={data}
            onChange={(values, items) => console.log(values, items)}
        />
    );
};

分组

将 type 设为 groupList

分组的 dataSource,一级子元素必须拥有 title 以及 children 属性,结构参考

暂不支持多层嵌套

import React from 'react';
import { Transfer } from '@douyinfe/semi-ui';

() => {
    const dataWithGroup = [
        {
            title: '类别A',
            children: [
                { label: 'A-1', value: 1, disabled: false, key: 1 },
                { label: 'A-2', value: 2, disabled: false, key: 2 },
                { label: 'A-3', value: 3, disabled: false, key: 3 },
            ],
        },
        {
            title: '类别B',
            children: [
                { label: 'B-1', value: 4, disabled: false, key: 4 },
                { label: 'B-2', value: 5, disabled: false, key: 5 },
                { label: 'B-3(disabled)', value: 6, disabled: true, key: 6 },
            ],
        },
        {
            title: '类别C',
            children: [
                { label: 'C-1', value: 7, disabled: false, key: 7 },
                { label: 'C-2', value: 8, disabled: false, key: 8 },
                { label: 'C-3', value: 9, disabled: false, key: 9 },
                { label: 'C-4', value: 10, disabled: false, key: 10 },
                { label: 'C-5', value: 11, disabled: false, key: 11 },
                { label: 'C-6', value: 12, disabled: false, key: 12 },
                { label: 'C-7', value: 13, disabled: false, key: 13 },
            ],
        },
    ];
    return (
        <Transfer
            type="groupList"
            defaultValue={[6]}
            style={{ width: 568 }}
            dataSource={dataWithGroup}
            onChange={(values, items) => console.log(values, items)}
        />
    );
};

自定义筛选逻辑,自定义选项数据渲染

使用filter自定义搜索逻辑,返回 true 时表示当前项符合筛选规则,保留当前项在列表中的显示,返回 false 则表示不符合,当前项会被隐藏
使用renderSourceItem,你可以自定义左侧每一条源数据的渲染结构
使用renderSelectedItem 你可以自定义右侧每一条已选项的渲染结构

import React from 'react';
import { Transfer, Checkbox, Avatar } from '@douyinfe/semi-ui';
import { IconClose } from '@douyinfe/semi-icons';

() => {
    const renderSourceItem = item => {
        return (
            <div className="components-transfer-demo-source-item" key={item.label}>
                <Checkbox
                    onChange={() => {
                        item.onChange();
                    }}
                    key={item.label}
                    checked={item.checked}
                    style={{ height: 52 }}
                >
                    <Avatar color={item.color} size="small">
                        {item.abbr}
                    </Avatar>
                    <div className="info">
                        <div className="name">{item.label}</div>
                        <div className="email">{item.value}</div>
                    </div>
                </Checkbox>
            </div>
        );
    };

    const renderSelectedItem = item => {
        return (
            <div className="components-transfer-demo-selected-item" key={item.label}>
                <Avatar color={item.color} size="small">
                    {item.abbr}
                </Avatar>
                <div className="info">
                    <div className="name">{item.label}</div>
                    <div className="email">{item.value}</div>
                </div>
                <IconClose onClick={item.onRemove} />
            </div>
        );
    };

    const customFilter = (sugInput, item) => {
        return item.value.includes(sugInput) || item.label.includes(sugInput);
    };

    const data = [
        { label: '夏可漫', value: '[email protected]', abbr: '夏', color: 'amber', area: 'US', key: 1 },
        { label: '申悦', value: '[email protected]', abbr: '申', color: 'indigo', area: 'UK', key: 2 },
        { label: '文嘉茂', value: '[email protected]', abbr: '文', color: 'cyan', area: 'HK', key: 3 },
        { label: '曲晨一', value: '[email protected]', abbr: '曲', color: 'blue', area: 'India', key: 4 },
        { label: '曲晨二', value: '[email protected]', abbr: '二', color: 'blue', area: 'India', key: 5 },
        { label: '曲晨三', value: '[email protected]', abbr: '三', color: 'blue', area: 'India', key: 6 },
    ];

    return (
        <Transfer
            style={{ width: 568 }}
            dataSource={data}
            filter={customFilter}
            defaultValue={['[email protected]', '[email protected]']}
            renderSelectedItem={renderSelectedItem}
            renderSourceItem={renderSourceItem}
            inputProps={{ placeholder: '搜索姓名或邮箱' }}
            onChange={(values, items) => console.log(values, items)}
        />
    );
};
.components-transfer-demo-selected-item {
    .semi-icon-close {
        visibility: hidden;
        color: var(--semi-color-tertiary);
    }
    &:hover {
        .semi-icon-close {
            visibility: visible;
        }
    }
}

.components-transfer-demo-selected-item,
.components-transfer-demo-source-item {
    height: 52px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 12px;
    &:hover {
        background-color: var(--semi-color-fill-0);
    }
    .info {
        margin-left: 8px;
        flex-grow: 1;
    }
    .name {
        font-size: 14px;
        line-height: 20px;
    }
    .email {
        font-size: 12px;
        line-height: 16px;
        color: var(--semi-color-text-2);
    }
}

禁用

import React from 'react';
import { Transfer } from '@douyinfe/semi-ui';
() => {
    const data = Array.from({ length: 20 }, (v, i) => {
        return {
            label: `选项名称 ${i}`,
            value: i,
            disabled: false,
            key: i,
        };
    });
    return (
        <Transfer
            style={{ width: 568, height: 416 }}
            disabled
            dataSource={data}
            defaultValue={[2, 4]}
            onChange={(values, items) => console.log(values, items)}
        />
    );
};

拖拽排序

draggable设为 true,开启拖拽排序功能。v1.11.0 后支持

import React from 'react';
import { Transfer } from '@douyinfe/semi-ui';
() => {
    const data = Array.from({ length: 30 }, (v, i) => {
        return {
            label: `选项名称 ${i}`,
            value: i,
            disabled: false,
            key: i,
        };
    });
    return (
        <Transfer
            style={{ width: 568, height: 416 }}
            dataSource={data}
            defaultValue={[2, 4]}
            draggable
            onChange={(values, items) => console.log(values, items)}
        />
    );
};

拖拽 + 自定义已选项渲染

draggable设为 true,开启拖拽排序功能;使用 renderSelectedItem 自定义右侧已选项渲染;
你可以将触发器定义为任意你想要的ReactNode,并且添加样式。将拖拽触发器,使用 sortableHandle 进行包裹即可(sortableHandle于 v 1.22.0 后提供),

import React from 'react';
import { Transfer, Checkbox, Avatar } from '@douyinfe/semi-ui';
import { IconHandle, IconClose } from '@douyinfe/semi-icons';

() => {
    const renderSourceItem = item => {
        return (
            <div className="components-transfer-demo-source-item" key={item.label}>
                <Checkbox
                    onChange={() => {
                        item.onChange();
                    }}
                    key={item.label}
                    checked={item.checked}
                    style={{ height: 52 }}
                >
                    <Avatar color={item.color} size="small">
                        {item.abbr}
                    </Avatar>
                    <div className="info">
                        <div className="name">{item.label}</div>
                        <div className="email">{item.value}</div>
                    </div>
                </Checkbox>
            </div>
        );
    };

    const renderSelectedItem = item => {
        const { sortableHandle } = item;
        const DragHandle = sortableHandle(() => <IconHandle className={`semi-right-item-drag-handler`} />); 
        return (
            <div className="components-transfer-demo-selected-item" key={item.label}>
                <DragHandle />
                <Avatar color={item.color} size="small">
                    {item.abbr}
                </Avatar>
                <div className="info">
                    <div className="name">{item.label}</div>
                    <div className="email">{item.value}</div>
                </div>
                <IconClose onClick={item.onRemove} />
            </div>
        );
    };

    const customFilter = (sugInput, item) => {
        return item.value.includes(sugInput) || item.label.includes(sugInput);
    };

    const data = [
        { label: '夏可漫', value: '[email protected]', abbr: '夏', color: 'amber', area: 'US', key: 1 },
        { label: '申悦', value: '[email protected]', abbr: '申', color: 'indigo', area: 'UK', key: 2 },
        { label: '文嘉茂', value: '[email protected]', abbr: '文', color: 'cyan', area: 'HK', key: 3 },
        { label: '曲晨一', value: '[email protected]', abbr: '曲', color: 'blue', area: 'India', key: 4 },
        { label: '曲晨二', value: '[email protected]', abbr: '二', color: 'blue', area: 'India', key: 5 },
        { label: '曲晨三', value: '[email protected]', abbr: '三', color: 'blue', area: 'India', key: 6 },
    ];

    return (
        <Transfer
            draggable
            style={{ width: 568 }}
            dataSource={data}
            filter={customFilter}
            defaultValue={['[email protected]', '[email protected]']}
            renderSelectedItem={renderSelectedItem}
            renderSourceItem={renderSourceItem}
            inputProps={{ placeholder: '搜索姓名或邮箱' }}
            onChange={(values, items) => console.log(values, items)}
        />
    );
};

完全自定义渲染

Semi 提供了 renderSourcePanelrenderSelectedPanel 入参,允许你完全自定义左右侧两个面板的渲染结构
通过该功能,你可以直接复用 Transfer 内部的逻辑能力,实现高度自定义样式结构的Transfer组件 renderSourcePanel: (sourcePanelProps: SourcePanelProps) => ReactNode
SourcePanelProps包含以下参数,你可以从中获取数据来渲染出你的 Panel 结构

interface SourcePanelProps {
  value: Array<string|number>; // 所有选中项的key
  loading: boolean;  // 是否加载中
  noMatch: boolean;  // 是否没有符合当前搜索值匹配的项
  filterData: Array<Item> // 匹配当前搜索值的项
  sourceData: Array<Item>; // 所有项
  allChecked: boolean; // 是否全部选中
  showNumber: number; // 筛选结果数量
  inputValue: string; // input搜索框的值
  onSearch: (searchString: string) => any; // 搜索框变化时应调用的函数,入参为搜索值
  onAllClick: () => void; // 左侧全部按钮点击时应调用的函数
  onSelectOrRemove: (item: Item) => any; //选择、删除单个选项时应调用的函数,入参应为当前操作item
  onSelect: (value:Array<string|number>)=>void; // 受控批量选中key
  selectedItem: Map, // 所有已选项的集合
}

renderSelectedPanel: (selectedPanelProps: SelectedPanelProps) => ReactNode
SelectedPanelProps包含以下参数

interface SelectedPanelProps {
  length: number; // 已选项的数量
  onClear: () => void; // 点击清空时应调用的回调函数
  onRemove: (item: Item) => void; // 删除单个选项时应调用的函数
  onSortEnd: ({ oldIndex, newIndex}) => void; // 对结果重新排序时应调用的函数
  selectedData: Array<Item>; // 所有已选项集合
}
import React from 'react';
import { Transfer, Input, Spin, Button } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';

class CustomRenderDemo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dataSource: Array.from({ length: 100 }, (v, i) => ({
                label: `海底捞门店 ${i}`,
                value: i,
                disabled: false,
                key: i,
            })),
        };
        this.renderSourcePanel = this.renderSourcePanel.bind(this);
        this.renderSelectedPanel = this.renderSelectedPanel.bind(this);
        this.renderItem = this.renderItem.bind(this);
    }

    renderItem(type, item, onItemAction, selectedItems) {
        let buttonText = '删除';
        if (type === 'source') {
            let checked = selectedItems.has(item.key);
            buttonText = checked ? '删除' : '添加';
        }
        return (
            <div className="semi-transfer-item panel-item" key={item.label}>
                <p>{item.label}</p>
                <Button
                    theme="borderless"
                    type="primary"
                    onClick={() => onItemAction(item)}
                    className="panel-item-remove"
                    size="small"
                >
                    {buttonText}
                </Button>
            </div>
        );
    }

    renderSourcePanel(props) {
        const {
            loading,
            noMatch,
            filterData,
            selectedItems,
            allChecked,
            onAllClick,
            inputValue,
            onSearch,
            onSelectOrRemove,
        } = props;
        let content;
        switch (true) {
            case loading:
                content = <Spin loading />;
                break;
            case noMatch:
                content = <div className="empty sp-font">{inputValue ? '无搜索结果' : '暂无内容'}</div>;
                break;
            case !noMatch:
                content = filterData.map(item => this.renderItem('source', item, onSelectOrRemove, selectedItems));
                break;
            default:
                content = null;
                break;
        }
        return (
            <section className="source-panel">
                <div className="panel-header sp-font">门店列表</div>
                <div className="panel-main">
                    <Input
                        style={{ width: 454, margin: '12px 14px' }}
                        prefix={<IconSearch />}
                        onChange={onSearch}
                        showClear
                    />
                    <div className="panel-controls sp-font">
                        <span>待选门店: {filterData.length}</span>
                        <Button onClick={onAllClick} theme="borderless" size="small">
                            {allChecked ? '取消全选' : '全选'}
                        </Button>
                    </div>
                    <div className="panel-list">{content}</div>
                </div>
            </section>
        );
    }

    renderSelectedPanel(props) {
        const { selectedData, onClear, clearText, onRemove } = props;

        let mainContent = selectedData.map(item => this.renderItem('selected', item, onRemove));

        if (!selectedData.length) {
            mainContent = <div className="empty sp-font">暂无数据,请从左侧筛选</div>;
        }

        return (
            <section className="selected-panel">
                <div className="panel-header sp-font">
                    <div>已选同步门店: {selectedData.length}</div>
                    <Button theme="borderless" type="primary" onClick={onClear} size="small">
                        {clearText || '清空 '}
                    </Button>
                </div>
                <div className="panel-main">{mainContent}</div>
            </section>
        );
    }

    render() {
        const { dataSource } = this.state;
        return (
            <Transfer
                onChange={values => console.log(values)}
                className="component-transfer-demo-custom-panel"
                renderSourcePanel={this.renderSourcePanel}
                renderSelectedPanel={this.renderSelectedPanel}
                dataSource={dataSource}
            />
        );
    }
}
.component-transfer-demo-custom-panel {

    .sp-font {
        color: rgba(var(--semi-grey-9), 1);
        font-size: 12px;
        font-weight: 500;
        line-height: 20px;
    }

    .empty {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .panel-item {
        flex-shrink: 0;
        height: 56px;
        border-radius: 4px;
        padding: 8px 12px;
        flex-wrap: wrap;
        background-color: rgba(22, 24, 35, .03);

        &-main {
            flex-grow: 1;
        }

        p {
            margin: 0 12px;
            flex-basis: 100%;
        }

        .panel-item-remove {
            cursor: pointer;
            color: var(--semi-color-primary);
        }
    }

    .panel-header {
        padding: 10px 12px;
        border: 1px solid rgba(22, 24, 35, .16);
        border-radius: 4px 4px 0 0;
        height: 38px;
        box-sizing: border-box;
        background-color: var(--semi-color-tertiary-light-default);
        display: flex;
        align-items: center;
        justify-content: space-between;

        .clear {
            cursor: pointer;
            color: var(--semi-color-primary);
        }
    }

    .source-panel {
        display: flex;
        flex-direction: column;
        width: 482px;
        height: 353px;

        .panel-main {
            border: 1px solid var(--semi-color-border);
            border-top: none;

            .panel-list {
                display: flex;
                flex-wrap: wrap;
                row-gap: 8px;
                column-gap: 8px;
                overflow-y: auto;
                height: 214px;
                margin-left: 12px;
                margin-right: 12px;
                padding-bottom: 8px;
            }
        }

        .panel-controls {
            margin: 10px 12px;
            font-size: 12px;
            line-height: 20px;

            .semi-button {
                margin-left: 8px;
                font-size: 12px;
            }
        }

        .panel-item {
            width: 176px;
        }

        margin-right: 16px;
    }

    .selected-panel {
        width: 200px;
        height: 353px;

        .panel-main {
            display: flex;
            flex-direction: column;
            overflow-y: auto;
            padding: 12px;
            border: 1px solid var(--semi-color-border);
            border-top: none;
            height: 323px;
            box-sizing: border-box;
            row-gap: 8px;
        }
    }
}

完全自定义渲染 、 拖拽排序

在完全自定义渲染的场景下,由于拖拽区的渲染也已由你完全接管,因此你不声明 draggable 亦可。
但你需要自行实现拖拽逻辑,我们推荐直接使用react-sortable-hoc
要支持拖拽排序,你需要在拖拽排序结束后,将 oldIndex、newIndex 作为入参,调用 onSortEnd

import React from 'react';
import { SortableContainer, SortableElement, sortableHandle } from 'react-sortable-hoc';
import { Transfer, Button, Spin, Input } from '@douyinfe/semi-ui';
import { IconHandle, IconSearch } from '@douyinfe/semi-icons';

class CustomRenderDragDemo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dataSource: Array.from({ length: 100 }, (v, i) => ({
                label: `海底捞门店 ${i}`,
                value: i,
                disabled: false,
                key: i,
            })),
        };
        this.renderSourcePanel = this.renderSourcePanel.bind(this);
        this.renderSelectedPanel = this.renderSelectedPanel.bind(this);
        this.renderItem = this.renderItem.bind(this);
    }

    renderItem(type, item, onItemAction, selectedItems) {
        let buttonText = '删除';
        let newItem = item;

        if (type === 'source') {
            let checked = selectedItems.has(item.key);
            buttonText = checked ? '删除' : '添加';
        } else {
            // delete newItem._optionKey;
            newItem = { ...item, key: item._optionKey };
            delete newItem._optionKey;
        }

        const DragHandle = sortableHandle(() => <IconHandle className="pane-item-drag-handler" />);

        return (
            <div className="semi-transfer-item panel-item" key={item.label}>
                {type === 'source' ? null : <DragHandle />}
                <div className="panel-item-main" style={{ flexGrow: 1 }}>
                    <p>{item.label}</p>
                    <Button
                        theme="borderless"
                        type="primary"
                        onClick={() => onItemAction(newItem)}
                        className="panel-item-remove"
                        size="small"
                    >
                        {buttonText}
                    </Button>
                </div>
            </div>
        );
    }

    renderSourcePanel(props) {
        const {
            loading,
            noMatch,
            filterData,
            selectedItems,
            allChecked,
            onAllClick,
            inputValue,
            onSearch,
            onSelectOrRemove,
        } = props;
        let content;
        switch (true) {
            case loading:
                content = <Spin loading />;
                break;
            case noMatch:
                content = <div className="empty sp-font">{inputValue ? '无搜索结果' : '暂无内容'}</div>;
                break;
            case !noMatch:
                content = filterData.map(item => this.renderItem('source', item, onSelectOrRemove, selectedItems));
                break;
            default:
                content = null;
                break;
        }
        return (
            <section className="source-panel">
                <div className="panel-header sp-font">门店列表</div>
                <div className="panel-main">
                    <Input
                        style={{ width: 454, margin: '12px 14px' }}
                        prefix={<IconSearch />}
                        onChange={onSearch}
                        showClear
                    />
                    <div className="panel-controls sp-font">
                        <span>待选门店: {filterData.length}</span>
                        <Button onClick={onAllClick} theme="borderless" size="small">
                            {allChecked ? '取消全选' : '全选'}
                        </Button>
                    </div>
                    <div className="panel-list">{content}</div>
                </div>
            </section>
        );
    }

    renderSelectedPanel(props) {
        const { selectedData, onClear, clearText, onRemove, onSortEnd } = props;

        let mainContent = null;

        if (!selectedData.length) {
            mainContent = <div className="empty sp-font">暂无数据,请从左侧筛选</div>;
        }

        const SortableItem = SortableElement(item => this.renderItem('selected', item, onRemove));
        const SortableList = SortableContainer(
            ({ items }) => {
                return (
                    <div className="panel-main">
                        {items.map((item, index) => (
                            // sortableElement will take over the property 'key', so use another '_optionKey' to pass
                            // otherwise you can't get `key` property in this.renderItem
                            <SortableItem key={item.label} index={index} {...item} _optionKey={item.key}></SortableItem>
                        ))}
                    </div>
                );
            },
            { distance: 10 }
        );

        mainContent = <SortableList useDragHandle onSortEnd={onSortEnd} items={selectedData}></SortableList>;

        return (
            <section className="selected-panel">
                <div className="panel-header sp-font">
                    <div>已选同步门店: {selectedData.length}</div>
                    <Button theme="borderless" type="primary" onClick={onClear} size="small">
                        {clearText || '清空 '}
                    </Button>
                </div>
                {mainContent}
            </section>
        );
    }

    render() {
        const { dataSource } = this.state;
        return (
            <Transfer
                defaultValue={[2, 4]}
                onChange={values => console.log(values)}
                className="component-transfer-demo-custom-panel"
                renderSourcePanel={this.renderSourcePanel}
                renderSelectedPanel={this.renderSelectedPanel}
                dataSource={dataSource}
            />
        );
    }
}

树穿梭框

传入 type 为treeList,使用Tree组件作为自定义渲染列表。v1.20.0 提供

可通过treeProps(TreeProps)来覆盖默认树的属性,左侧树默认属性为

{
    multiple:true,
    disableStrictly:true,
    leafOnly:true,
    filterTreeNode:true,
    searchRender:flase,

}
import React, { useState } from 'react';
import { Transfer } from '@douyinfe/semi-ui';

() => {
    const treeData = [
        {
            label: 'Asia',
            value: 'Asia',
            key: '0',
            children: [
                {
                    label: 'China',
                    value: 'China',
                    key: '0-0',
                    children: [
                        {
                            label: 'Beijing',
                            value: 'Beijing',
                            key: '0-0-0',
                        },
                        {
                            label: 'Shanghai',
                            value: 'Shanghai',
                            key: '0-0-1',
                        },
                        {
                            label: 'Chengdu',
                            value: 'Chengdu',
                            key: '0-0-2',
                        },
                    ],
                },
                {
                    label: 'Japan',
                    value: 'Japan',
                    key: '0-1',
                    children: [
                        {
                            label: 'Osaka',
                            value: 'Osaka',
                            key: '0-1-0',
                        },
                    ],
                },
            ],
        },
        {
            label: 'North America',
            value: 'North America',
            key: '1',
            children: [
                {
                    label: 'United States',
                    value: 'United States',
                    key: '1-0',
                },
                {
                    label: 'Canada',
                    value: 'Canada',
                    key: '1-1',
                },
                {
                    label: 'Mexico',
                    value: 'Mexico',
                    disabled: true,
                    key: '1-2',
                },
                {
                    label: 'Cuba',
                    value: 'Cuba',
                    key: '1-3',
                },
            ],
        },
    ];

    const [v, $v] = useState(['Shanghai']);

    return (
        <div style={{ margin: 10, padding: 10, width: 600 }}>
            <Transfer dataSource={treeData} type="treeList" value={v} onChange={$v}></Transfer>
        </div>
    );
};

Accessibility

ARIA

  • 搜索框添加 role search
  • 右侧选中列表添加 role list,选中项添加 role listitem

API 参考

Trasnfer Props

Item Interface

属性 说明 类型 默认值 版本
className 样式类名 string
dataSource 数据源 Array|Array|Array []
defaultValue 默认已选中值 Array
disabled 是否禁用 boolean false
draggable 是否开启拖拽排序 boolean false
emptyContent 自定义空状态的提示文本,search 为无搜索结果时展示的文本,left 为左侧无源数据时的文本,right 为无勾选数据时的提示文本 {left: ReactNode; right: ReactNode; search: ReactNode;}
filter 自定义筛选逻辑, 当为 false 时,不展示搜索框 boolean | (input:string, item: Item) => boolean true
inputProps 可用于自定义搜索框 Input,可配置属性参考 Input 组件 InputProps
loading 是否正在加载左侧选项 boolean -
onChange 选中值发生变化时触发的回调, 拖拽排序变化后也会触发该回调 (values: Array, items: Array) => void
onDeselect 取消勾选时的回调 (item: Item) => void
onSearch 搜索框输入内容变化时调用 (inputValue: string) => void
onSelect 勾选时的回调 (item: Item) => void
renderSelectedItem 自定义右侧单个已选项的渲染 (item: { onRemove, sortableHandle } & Item) => ReactNode
renderSelectedPanel 自定义右侧已选面板的渲染 (selectedPanelProps) => ReactNode 1.11.0
renderSourceItem 自定义左侧单个候选项的渲染 (item: { onChange, checked } & Item) => ReactNode
renderSourcePanel 自定义左侧候选面板的渲染 (sourcePanelProps) => ReactNode 1.11.0
showPath 当 type 为treeList时,控制右侧选中项是否显示选择路径 boolean false 1.20.0
style 内联样式 CSSProperties
treeProps 当 type 为treeList时,可作为 TreeProps 传入左侧的 Tree 组件 TreeProps 1.20.0
type Transfer 类型,可选listgroupListtreeList string 'list' 1.20.0
value 已选中值,传入该项时,将作为受控组件使用 Array
属性 说明 类型 默认值
className 样式类名 string
disabled 是否禁用 boolean false
key 必填,每个选项的唯一标识,不允许重复 string | number
label 选项展示内容 ReactNode
style 内联样式 CSSProperties
value 选项代表的值 string | number

GroupItem Interface

GroupItem继承Item的所有属性

TreeItem Interface

TreeItem 继承 Item 的所有属性

属性 说明 类型 默认值
children 该分组的元素 Array
title 分组名称 string

设计变量

属性 说明 类型 默认值
children 子元素 Array