From edb6cdad741e6b8c7595d3d7bcbb2153fc31b912 Mon Sep 17 00:00:00 2001 From: huskylengcb <649569822@qq.com> Date: Tue, 4 Aug 2020 17:38:45 +0800 Subject: [PATCH] feat(tree): add tree component (#51) affects: @gio-design/components, @gio-design/icons, @gio-design/tokens, website --- .../src/components/config-provider/index.tsx | 1 + .../components/src/components/tree/Tree.tsx | 127 ++++++++++++++++ .../src/components/tree/iconUtil.tsx | 26 ++++ .../components/src/components/tree/index.tsx | 15 ++ .../src/components/tree/style/index.less | 140 ++++++++++++++++++ .../src/components/tree/style/index.ts | 1 + packages/icons/src/CaretDownOutlined.tsx | 24 +++ packages/icons/src/LoadingOutlinedBlack.tsx | 18 +-- packages/icons/src/LoadingOutlinedWhite.tsx | 18 +-- packages/icons/src/More.tsx | 11 +- packages/icons/src/Warning.tsx | 15 +- packages/icons/src/index.tsx | 7 +- packages/icons/svgs/caret-down-outlined.svg | 1 + .../tokens/properties/color/background.json | 16 +- packages/tokens/properties/color/text.json | 27 +++- packages/tokens/properties/palette.json | 3 + packages/tokens/properties/radius.json | 3 + .../components/functional/tree/demos/icon.tsx | 72 +++++++++ .../functional/tree/demos/index.less | 14 ++ .../functional/tree/demos/index.tsx | 99 +++++++++++++ .../components/functional/tree/index.zh-CN.md | 60 ++++++++ 21 files changed, 657 insertions(+), 41 deletions(-) create mode 100644 packages/components/src/components/tree/Tree.tsx create mode 100644 packages/components/src/components/tree/iconUtil.tsx create mode 100644 packages/components/src/components/tree/index.tsx create mode 100644 packages/components/src/components/tree/style/index.less create mode 100644 packages/components/src/components/tree/style/index.ts create mode 100644 packages/icons/src/CaretDownOutlined.tsx create mode 100644 packages/icons/svgs/caret-down-outlined.svg create mode 100644 packages/website/src/components/functional/tree/demos/icon.tsx create mode 100644 packages/website/src/components/functional/tree/demos/index.less create mode 100644 packages/website/src/components/functional/tree/demos/index.tsx create mode 100644 packages/website/src/components/functional/tree/index.zh-CN.md diff --git a/packages/components/src/components/config-provider/index.tsx b/packages/components/src/components/config-provider/index.tsx index d21a7b0608..368c69cbef 100644 --- a/packages/components/src/components/config-provider/index.tsx +++ b/packages/components/src/components/config-provider/index.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; export interface ConfigConsumerProps { getPrefixCls: (suffixCls: string, customizePrefixCls?: string) => string; autoInsertSpaceInButton?: boolean; + virtual?: boolean; } export const ConfigContext = React.createContext({ diff --git a/packages/components/src/components/tree/Tree.tsx b/packages/components/src/components/tree/Tree.tsx new file mode 100644 index 0000000000..802bcedd5d --- /dev/null +++ b/packages/components/src/components/tree/Tree.tsx @@ -0,0 +1,127 @@ +import * as React from 'react'; +import RcTree, { TreeNode, TreeProps as RcTreeProps } from 'rc-tree'; +import classNames from 'classnames'; +import { DataNode, Key } from 'rc-tree/lib/interface'; +import { ConfigContext } from '../config-provider'; +import renderSwitcherIcon from './iconUtil'; + +export interface GioTreeNodeAttribute { + eventKey: string; + prefixCls: string; + className: string; + expanded: boolean; + selected: boolean; + children: React.ReactNode; + title: React.ReactNode; + pos: string; + isLeaf: boolean; + selectable: boolean; + disabled: boolean; +} + +export interface GioTreeNodeProps { + className?: string; + disabled?: boolean; + title?: string | React.ReactNode; + key?: Key; + eventKey?: string; + isLeaf?: boolean; + checked?: boolean; + expanded?: boolean; + selected?: boolean; + selectable?: boolean; + icon?: ((treeNode: GioTreeNodeAttribute) => React.ReactNode) | React.ReactNode; + children?: React.ReactNode; + [customProp: string]: any; +} + +export type GioTreeNode = React.Component; + +export interface GioTreeNodeBaseEvent { + node: GioTreeNode; + nativeEvent: MouseEvent; +} + +export interface GioTreeNodeSelectedEvent extends GioTreeNodeBaseEvent { + event: 'select'; + selected?: boolean; + selectedNodes?: DataNode[]; +} + +export interface GioTreeNodeExpandedEvent extends GioTreeNodeBaseEvent { + expanded?: boolean; +} + +export interface GioTreeNodeMouseEvent { + node: GioTreeNode; + event: React.DragEvent; +} + +export interface GioTreeNodeDragEnterEvent extends GioTreeNodeMouseEvent { + expandedKeys: Key[]; +} + +export type TreeNodeNormal = DataNode; + +export interface TreeProps extends Omit { + className?: string; + /** 是否自动展开父节点 */ + autoExpandParent?: boolean; + /** 是否禁用树 */ + disabled?: boolean; + /** 默认展开所有树节点 */ + defaultExpandAll?: boolean; + /** 默认展开对应树节点 */ + defaultExpandParent?: boolean; + /** 默认展开指定的树节点 */ + defaultExpandedKeys?: Key[]; + /** (受控)展开指定的树节点 */ + expandedKeys?: Key[]; + /** (受控)设置选中的树节点 */ + selectedKeys?: Key[]; + /** 默认选中的树节点 */ + defaultSelectedKeys?: Key[]; + selectable?: boolean; + loadedKeys?: Key[]; + style?: React.CSSProperties; + showIcon?: boolean; + icon?: ((nodeProps: GioTreeNodeAttribute) => React.ReactNode) | React.ReactNode; + switcherIcon?: React.ReactElement; + prefixCls?: string; + children?: React.ReactNode; +} + +interface CompoundedComponent extends React.ForwardRefExoticComponent> { + TreeNode: typeof TreeNode; +} + +const Tree = React.forwardRef((props, ref) => { + const { getPrefixCls, virtual } = React.useContext(ConfigContext); + const { prefixCls: customizePrefixCls, className, showIcon, children, switcherIcon } = props; + + const newProps = { ...props }; + const prefixCls = getPrefixCls('tree', customizePrefixCls); + + return ( + renderSwitcherIcon(prefixCls, switcherIcon, nodeProps)} + > + {children} + + ); +}) as CompoundedComponent; + +Tree.TreeNode = TreeNode; + +Tree.defaultProps = { + showIcon: false, +}; + +export default Tree; diff --git a/packages/components/src/components/tree/iconUtil.tsx b/packages/components/src/components/tree/iconUtil.tsx new file mode 100644 index 0000000000..40ee572a25 --- /dev/null +++ b/packages/components/src/components/tree/iconUtil.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import { CaretDownOutlined } from '@gio-design/icons'; +import { GioTreeNodeProps } from './Tree'; + +export default function renderSwitcherIcon( + prefixCls: string, + switcherIcon: React.ReactNode | null | undefined, + { isLeaf }: GioTreeNodeProps +) { + if (isLeaf) { + return null; + } + const switcherCls = `${prefixCls}-switcher-icon`; + if (React.isValidElement(switcherIcon)) { + return React.cloneElement(switcherIcon, { + className: classNames(switcherIcon?.props.className || '', switcherCls), + }); + } + + if (switcherIcon) { + return switcherIcon; + } + + return ; +} diff --git a/packages/components/src/components/tree/index.tsx b/packages/components/src/components/tree/index.tsx new file mode 100644 index 0000000000..3b41546b75 --- /dev/null +++ b/packages/components/src/components/tree/index.tsx @@ -0,0 +1,15 @@ +import Tree from './Tree'; + +export { EventDataNode } from 'rc-tree/lib/interface'; + +export { + TreeProps, + TreeNodeNormal, + GioTreeNode, + GioTreeNodeMouseEvent, + GioTreeNodeExpandedEvent, + GioTreeNodeSelectedEvent, + GioTreeNodeProps, +} from './Tree'; + +export default Tree; diff --git a/packages/components/src/components/tree/style/index.less b/packages/components/src/components/tree/style/index.less new file mode 100644 index 0000000000..f67196d9f9 --- /dev/null +++ b/packages/components/src/components/tree/style/index.less @@ -0,0 +1,140 @@ +@import '../../../stylesheet/theme.less'; +@import '../../../stylesheet/mixin/index.less'; +@import '~@gio-design/tokens/dist/variables.less'; + +@tree-prefix-cls: ~'@{component-prefix}-tree'; +@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode'; + +.antTreeFn(@tree-prefix-cls); + +.antTreeFn(@custom-tree-prefix-cls) { + @custom-tree-node-prefix-cls: ~'@{custom-tree-prefix-cls}-treenode'; + .@{custom-tree-prefix-cls} { + .reset-component; + background: @color-background-tree-normal; + border-radius: @radius-border-tree; + transition: background-color 0.3s; + + // =================== Virtual List =================== + &-list-holder-inner { + align-items: flex-start; + } + + // ===================== TreeNode ===================== + .@{custom-tree-node-prefix-cls} { + display: flex; + align-items: flex-start; + padding: 0; + outline: none; + width: 100%; + height: 40px; + line-height: 40px; + border-radius: @radius-border-tree; + margin: 2px 0; + font-size: 14px; + color: @color-text-tree-normal; + font-weight: 400; + &:hover { + background-color: @color-background-tree-hover; + } + // Disabled + &-disabled { + .@{custom-tree-prefix-cls}-node-content-wrapper { + color: @color-text-tree-disable; + cursor: not-allowed; + .@{custom-tree-prefix-cls}-iconEle { + svg { + color: @color-text-tree-disable; + path { + fill: @color-text-tree-disable; + } + } + } + } + } + // Selected + &-selected { + background-color: @color-background-tree-active; + .@{custom-tree-prefix-cls}-node-content-wrapper { + color: @color-text-tree-selected; + .@{custom-tree-prefix-cls}-iconEle { + svg { + color: @color-text-tree-selected; + path { + fill: @color-text-tree-selected; + } + } + } + } + } + } + + // >>> Indent + &-indent { + align-self: stretch; + white-space: nowrap; + user-select: none; + + &-unit { + display: inline-block; + width: 24px; + } + } + + // >>> Switcher + & &-switcher { + flex: none; + width: 14px; + height: 14px; + margin: 0; + line-height: 40px; + text-align: center; + margin: 2px 8px 0; + color: @color-text-tree-switcher; + cursor: pointer; + + &-noop { + cursor: default; + } + + svg.@{custom-tree-prefix-cls}-switcher-icon { + color: @color-text-tree-switcher; + path { + fill: @color-text-tree-switcher; + } + } + + &_close { + svg.@{custom-tree-prefix-cls}-switcher-icon { + transform: rotate(-90deg); + } + } + } + + // >>> Title + & &-node-content-wrapper { + width: 100%; + cursor: pointer; + + .@{custom-tree-prefix-cls}-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + // Icon + .@{custom-tree-prefix-cls}-iconEle { + display: inline-block; + width: 16px; + height: 16px; + line-height: 16px; + margin: 2px 8px 0 0; + text-align: center; + vertical-align: middle; + &:empty { + display: none; + } + } + } + } +} diff --git a/packages/components/src/components/tree/style/index.ts b/packages/components/src/components/tree/style/index.ts new file mode 100644 index 0000000000..d74e52ee9f --- /dev/null +++ b/packages/components/src/components/tree/style/index.ts @@ -0,0 +1 @@ +import './index.less'; diff --git a/packages/icons/src/CaretDownOutlined.tsx b/packages/icons/src/CaretDownOutlined.tsx new file mode 100644 index 0000000000..28e9d1674f --- /dev/null +++ b/packages/icons/src/CaretDownOutlined.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; + +function SvgCaretDownOutlined(props: React.SVGProps) { + return ( + + + + + + + + + + + ); +} + +const MemoSvgCaretDownOutlined = React.memo(SvgCaretDownOutlined); +export default MemoSvgCaretDownOutlined; diff --git a/packages/icons/src/LoadingOutlinedBlack.tsx b/packages/icons/src/LoadingOutlinedBlack.tsx index 04d8e3be90..a1c75b68f4 100644 --- a/packages/icons/src/LoadingOutlinedBlack.tsx +++ b/packages/icons/src/LoadingOutlinedBlack.tsx @@ -1,17 +1,15 @@ -import React from 'react'; +import * as React from 'react'; -function LoadingOutlinedBlack(props: React.SVGProps) { +function SvgLoadingOutlinedBlack(props: React.SVGProps) { return ( - - - - - - + + + + ); } -const MemoLoadingOutlinedBlack = React.memo(LoadingOutlinedBlack); -export default MemoLoadingOutlinedBlack; +const MemoSvgLoadingOutlinedBlack = React.memo(SvgLoadingOutlinedBlack); +export default MemoSvgLoadingOutlinedBlack; diff --git a/packages/icons/src/LoadingOutlinedWhite.tsx b/packages/icons/src/LoadingOutlinedWhite.tsx index 053d6d0d25..05feb68208 100644 --- a/packages/icons/src/LoadingOutlinedWhite.tsx +++ b/packages/icons/src/LoadingOutlinedWhite.tsx @@ -1,17 +1,15 @@ -import React from 'react'; +import * as React from 'react'; -function LoadingOutlinedWhite(props: React.SVGProps) { +function SvgLoadingOutlinedWhite(props: React.SVGProps) { return ( - - - - - - + + + + ); } -const MemoLoadingOutlinedWhite = React.memo(LoadingOutlinedWhite); -export default MemoLoadingOutlinedWhite; +const MemoSvgLoadingOutlinedWhite = React.memo(SvgLoadingOutlinedWhite); +export default MemoSvgLoadingOutlinedWhite; diff --git a/packages/icons/src/More.tsx b/packages/icons/src/More.tsx index 8c40c8fb7f..2f7e4f3a2e 100644 --- a/packages/icons/src/More.tsx +++ b/packages/icons/src/More.tsx @@ -3,12 +3,11 @@ import * as React from 'react'; function SvgMore(props: React.SVGProps) { return ( - - - + ); } diff --git a/packages/icons/src/Warning.tsx b/packages/icons/src/Warning.tsx index c1e7d9fa11..2d3143ba60 100644 --- a/packages/icons/src/Warning.tsx +++ b/packages/icons/src/Warning.tsx @@ -1,17 +1,12 @@ import * as React from 'react'; function SvgWarning(props: React.SVGProps) { - const { style, ...rest } = props; return ( - - - - - - + + ); } diff --git a/packages/icons/src/index.tsx b/packages/icons/src/index.tsx index f9b4379a8c..504f79ddea 100644 --- a/packages/icons/src/index.tsx +++ b/packages/icons/src/index.tsx @@ -2,12 +2,13 @@ export { default as AppFilled } from './AppFilled'; export { default as App } from './App'; export { default as AppsFilled } from './AppsFilled'; export { default as Calendar } from './Calendar'; +export { default as CaretDownOutlined } from './CaretDownOutlined'; export { default as CheckCircleFilled } from './CheckCircleFilled'; export { default as CheckSquareFilled } from './CheckSquareFilled'; export { default as Check } from './Check'; +export { default as LoadingOutlinedBlack } from './LoadingOutlinedBlack'; +export { default as LoadingOutlinedWhite } from './LoadingOutlinedWhite'; +export { default as More } from './More'; export { default as SettingFilled } from './SettingFilled'; export { default as Setting } from './Setting'; -export { default as LoadingOutlinedWhite } from './LoadingOutlinedWhite'; -export { default as LoadingOutlinedBlack } from './LoadingOutlinedBlack'; export { default as Warning } from './Warning'; -export { default as More } from './More'; diff --git a/packages/icons/svgs/caret-down-outlined.svg b/packages/icons/svgs/caret-down-outlined.svg new file mode 100644 index 0000000000..28ad97497b --- /dev/null +++ b/packages/icons/svgs/caret-down-outlined.svg @@ -0,0 +1 @@ +down-2 \ No newline at end of file diff --git a/packages/tokens/properties/color/background.json b/packages/tokens/properties/color/background.json index 82347987dc..456fbb5433 100644 --- a/packages/tokens/properties/color/background.json +++ b/packages/tokens/properties/color/background.json @@ -84,6 +84,20 @@ "value": "{palette.gray.1.value}" } }, + "tree": { + "normal": { + "value": "{palette.white.value}" + }, + "hover": { + "value": "{palette.gray.1.value}" + }, + "active": { + "value": "{palette.gray.1.value}" + }, + "disable": { + "value": "{palette.gray.1.value}" + } + }, "loading": { "animation": { "0": { @@ -107,4 +121,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/tokens/properties/color/text.json b/packages/tokens/properties/color/text.json index 338cb90e08..c11afbe74c 100644 --- a/packages/tokens/properties/color/text.json +++ b/packages/tokens/properties/color/text.json @@ -93,6 +93,31 @@ "value": "{palette.gray.5.value}" } }, + "tree": { + "normal": { + "value": "{palette.dark.1.value}" + }, + "disable": { + "value": "{palette.gray.5.value}" + }, + "selected": { + "value": "{palette.blue.7.value}" + }, + "switcher": { + "value": "{palette.gray.10.value}" + }, + "loading": { + "value": "{palette.dark.1.value}" + }, + "popconfirm": { + "title": { + "value": "{palette.dark.3.value}" + }, + "desc": { + "value": "{palette.dark.1.value}" + } + } + }, "loading": { "value": "{palette.dark.1.value}" }, @@ -106,4 +131,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/tokens/properties/palette.json b/packages/tokens/properties/palette.json index 7dd031ce15..8876175d21 100644 --- a/packages/tokens/properties/palette.json +++ b/packages/tokens/properties/palette.json @@ -61,6 +61,9 @@ }, "9": { "value": "#F1F2F8" + }, + "10": { + "value": "#5C4E61" } }, "green": { diff --git a/packages/tokens/properties/radius.json b/packages/tokens/properties/radius.json index e2713327c2..b91739e6aa 100644 --- a/packages/tokens/properties/radius.json +++ b/packages/tokens/properties/radius.json @@ -18,6 +18,9 @@ }, "button": { "value": "{radius.border.small.value}" + }, + "tree": { + "value": "{radius.border.small.value}" } } } diff --git a/packages/website/src/components/functional/tree/demos/icon.tsx b/packages/website/src/components/functional/tree/demos/icon.tsx new file mode 100644 index 0000000000..0f8a0c3f51 --- /dev/null +++ b/packages/website/src/components/functional/tree/demos/icon.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import Tree from '@gio-design/components/es/components/tree'; +import '@gio-design/components/es/components/tree/style/css.js'; +import { App, Setting, Check } from '@gio-design/icons'; +import './index.less'; + +const treeData = [ + { + title: '产研团队', + key: '0-0', + icon: , + children: [ + { + title: '服务端', + key: '0-0-0', + disabled: true, + children: [ + { + title: 'Java开发', + key: '0-0-0-0', + disableCheckbox: true, + }, + { + title: 'Ruby开发', + key: '0-0-0-1', + icon: , + }, + ], + }, + { + title: '前端', + key: '0-0-1', + children: [ + { + title: 前端一组, + key: '0-0-1-0', + }, + ], + }, + ], + }, + { + title: 'CSM 团队', + key: '0-1', + children: [], + }, +]; + +const Demo: React.FC<{}> = () => { + const onSelect = (selectedKeys: any[], info: any) => { + console.log('selected', selectedKeys, info); + }; + + return ( +
+
+ + onSelect(selectedKeys, info) + } + treeData={treeData} + showIcon + icon={} + /> +
+
+ ); +}; + +export default Demo; diff --git a/packages/website/src/components/functional/tree/demos/index.less b/packages/website/src/components/functional/tree/demos/index.less new file mode 100644 index 0000000000..d8ee3d6501 --- /dev/null +++ b/packages/website/src/components/functional/tree/demos/index.less @@ -0,0 +1,14 @@ +.tree-demo { + width: 300px; + height: auto; + border: 1px dashed #333333; + padding: 20px; + float: left; + margin-right: 10px; + .title-icon { + float: right; + position: relative; + top: 13px; + right: 8px; + } +} \ No newline at end of file diff --git a/packages/website/src/components/functional/tree/demos/index.tsx b/packages/website/src/components/functional/tree/demos/index.tsx new file mode 100644 index 0000000000..b0695e0a92 --- /dev/null +++ b/packages/website/src/components/functional/tree/demos/index.tsx @@ -0,0 +1,99 @@ +import React, { useState } from 'react'; +import Tree from '@gio-design/components/es/components/tree'; +import '@gio-design/components/es/components/tree/style/css.js'; +import { Setting } from '@gio-design/icons'; +import './index.less'; + +const treeData = [ + { + title: '产品团队', + key: '0-0', + children: [ + { + title: '产品经理团队', + key: '0-0-0', + disabled: true, + children: [ + { + title: '产品一组', + key: '0-0-0-0', + disableCheckbox: true, + }, + { + title: '产品二组', + key: '0-0-0-1', + }, + ], + }, + { + title: '设计师团队', + key: '0-0-1', + children: [ + { + title: UX, + key: '0-0-1-0', + }, + ], + }, + ], + }, + { + title: '市场团队', + key: '0-1', + children: [], + }, +]; + +const Demo: React.FC<{}> = () => { + const [keys, setKeys] = useState([]); + + const onSelect = (selectedKeys: string[], info: any) => { + if (selectedKeys.length === 0) { + return; + } + setKeys(selectedKeys); + }; + + const titleContent = (title: any) => ( + <> + {title} + + + ); + + return ( +
+
+ + onSelect(selectedKeys, info) + } + selectedKeys={keys} + treeData={treeData} + /> +
+
+ + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default Demo; diff --git a/packages/website/src/components/functional/tree/index.zh-CN.md b/packages/website/src/components/functional/tree/index.zh-CN.md new file mode 100644 index 0000000000..b31ad19268 --- /dev/null +++ b/packages/website/src/components/functional/tree/index.zh-CN.md @@ -0,0 +1,60 @@ +--- +title: Tree 树形组件 +nav: + order: 2 + title: 组件 +group: + title: 功能组件 + order: 2 +--- + +# 树形控件 - Tree + +## 何时使用 + +文件夹、组织架构、生物分类、国家地区等等,世间万物的大多数结构都是树形结构。使用 树控件 可以完整展现其中的层级关系,并具有展开收起选择等交互功能。 + +## 代码演示 + + + + + + +## 参数说明 + +### Tree props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| autoExpandParent | 是否自动展开父节点 | boolean | true | +| defaultExpandAll | 默认展开所有树节点 | boolean | false | +| defaultExpandedKeys | 默认展开指定的树节点 | string[] | [] | +| defaultExpandParent | 默认展开父节点 | boolean | true | +| defaultSelectedKeys | 默认选中的树节点 | string[] | [] | +| disabled | 将树禁用 | boolean | false | +| expandedKeys | (受控)展开指定的树节点 | string[] | [] | +| loadData | 异步加载数据 | function(node) | - | +| loadedKeys | (受控)已经加载的节点,需要配合 loadData 使用 | string[] | [] | +| selectable | 是否可选中 | boolean | [] | +| selectedKeys | (受控)已经加载的节点,需要配合 loadData 使用 | string[] | [] | +| showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式 | boolean | false | +| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(key 在整个树范围内唯一) | array<{key, title, children, [disabled, selectable]}> | - | +| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | +| onExpand | 展开/收起节点时触发 | function(expandedKeys, {expanded: bool, node}) | - | +| onLoad | 节点加载完毕时触发 | function(loadedKeys, {event, node}) | - | +| onRightClick | 响应右键点击 | function({event, node}) | - | +| onSelect | 点击树节点触发 | function(selectedKeys, e:{selected: bool, selectedNodes, node, event}) | - | +| icon | 自定义树节点图标。 | ReactNode | (props) => ReactNode | - | + + +### TreeNode props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| disabled | 禁掉响应 | boolean | false | +| icon | 自定义图标。可接收组件,props 为当前节点 props | ReactNode | (props) => ReactNode | - | +| isLeaf | 设置为叶子节点(设置了loadData时有效) | boolean | false | +| key | 设置为叶子节点(设置了loadData时有效) | boolean | false | +| selectable | 设置节点是否可被选中 | boolean | true | +| title | 标题 | string /| ReactNode | --- |