--- order: 6 title: zh-CN: 树穿梭框 en-US: Tree Transfer --- ## zh-CN 使用 Tree 组件作为自定义渲染列表。 ## en-US Customize render list with Tree component. ```tsx import { Transfer, Tree } from 'antd'; import type { TransferDirection, TransferItem } from 'antd/es/transfer'; import type { DataNode } from 'antd/es/tree'; import React, { useState } from 'react'; interface TreeTransferProps { dataSource: DataNode[]; targetKeys: string[]; onChange: (targetKeys: string[], direction: TransferDirection, moveKeys: string[]) => void; } // Customize Table Transfer const isChecked = (selectedKeys: (string | number)[], eventKey: string | number) => selectedKeys.includes(eventKey); const generateTree = (treeNodes: DataNode[] = [], checkedKeys: string[] = []): DataNode[] => treeNodes.map(({ children, ...props }) => ({ ...props, disabled: checkedKeys.includes(props.key as string), children: generateTree(children, checkedKeys), })); const TreeTransfer = ({ dataSource, targetKeys, ...restProps }: TreeTransferProps) => { const transferDataSource: TransferItem[] = []; function flatten(list: DataNode[] = []) { list.forEach(item => { transferDataSource.push(item as TransferItem); flatten(item.children); }); } flatten(dataSource); return ( item.title!} showSelectAll={false} > {({ direction, onItemSelect, selectedKeys }) => { if (direction === 'left') { const checkedKeys = [...selectedKeys, ...targetKeys]; return ( { onItemSelect(key as string, !isChecked(checkedKeys, key)); }} onSelect={(_, { node: { key } }) => { onItemSelect(key as string, !isChecked(checkedKeys, key)); }} /> ); } }} ); }; const treeData: DataNode[] = [ { key: '0-0', title: '0-0' }, { key: '0-1', title: '0-1', children: [ { key: '0-1-0', title: '0-1-0' }, { key: '0-1-1', title: '0-1-1' }, ], }, { key: '0-2', title: '0-3' }, ]; const App: React.FC = () => { const [targetKeys, setTargetKeys] = useState([]); const onChange = (keys: string[]) => { setTargetKeys(keys); }; return ; }; export default App; ```