• 树形控件
    • 何时使用
    • 代码演示
      • 受控操作示例
      • 基本用法
      • 自定义TreeNode字段
      • 自定义图标
      • 拖动示例
      • 异步数据加载
      • 连接线
      • 可搜索
      • 目录
  • API
    • Tree props
    • 事件
    • TreeNode props
    • DirectoryTree props
  • FAQ
    • 在 showLine 时,如何隐藏子节点图标?

    树形控件

    何时使用

    文件夹、组织架构、生物分类、国家地区等等,世间万物的大多数结构都是树形结构。使用树控件可以完整展现其中的层级关系,并具有展开收起选择等交互功能。

    代码演示

    Tree 树形控件 - 图1

    受控操作示例

    受控操作示例

    1. <template>
    2. <a-tree
    3. checkable
    4. @expand="onExpand"
    5. :expandedKeys="expandedKeys"
    6. :autoExpandParent="autoExpandParent"
    7. v-model="checkedKeys"
    8. @select="onSelect"
    9. :selectedKeys="selectedKeys"
    10. :treeData="treeData"
    11. />
    12. </template>
    13. <script>
    14. const treeData = [
    15. {
    16. title: '0-0',
    17. key: '0-0',
    18. children: [
    19. {
    20. title: '0-0-0',
    21. key: '0-0-0',
    22. children: [
    23. { title: '0-0-0-0', key: '0-0-0-0' },
    24. { title: '0-0-0-1', key: '0-0-0-1' },
    25. { title: '0-0-0-2', key: '0-0-0-2' },
    26. ],
    27. },
    28. {
    29. title: '0-0-1',
    30. key: '0-0-1',
    31. children: [
    32. { title: '0-0-1-0', key: '0-0-1-0' },
    33. { title: '0-0-1-1', key: '0-0-1-1' },
    34. { title: '0-0-1-2', key: '0-0-1-2' },
    35. ],
    36. },
    37. {
    38. title: '0-0-2',
    39. key: '0-0-2',
    40. },
    41. ],
    42. },
    43. {
    44. title: '0-1',
    45. key: '0-1',
    46. children: [
    47. { title: '0-1-0-0', key: '0-1-0-0' },
    48. { title: '0-1-0-1', key: '0-1-0-1' },
    49. { title: '0-1-0-2', key: '0-1-0-2' },
    50. ],
    51. },
    52. {
    53. title: '0-2',
    54. key: '0-2',
    55. },
    56. ];
    57. export default {
    58. data() {
    59. return {
    60. expandedKeys: ['0-0-0', '0-0-1'],
    61. autoExpandParent: true,
    62. checkedKeys: ['0-0-0'],
    63. selectedKeys: [],
    64. treeData,
    65. };
    66. },
    67. watch: {
    68. checkedKeys(val) {
    69. console.log('onCheck', val);
    70. },
    71. },
    72. methods: {
    73. onExpand(expandedKeys) {
    74. console.log('onExpand', expandedKeys);
    75. // if not set autoExpandParent to false, if children expanded, parent can not collapse.
    76. // or, you can remove all expanded children keys.
    77. this.expandedKeys = expandedKeys;
    78. this.autoExpandParent = false;
    79. },
    80. onCheck(checkedKeys) {
    81. console.log('onCheck', checkedKeys);
    82. this.checkedKeys = checkedKeys;
    83. },
    84. onSelect(selectedKeys, info) {
    85. console.log('onSelect', info);
    86. this.selectedKeys = selectedKeys;
    87. },
    88. },
    89. };
    90. </script>

    Tree 树形控件 - 图2

    基本用法

    最简单的用法,展示可勾选,可选中,禁用,默认展开等功能。

    1. <template>
    2. <a-tree
    3. checkable
    4. :treeData="treeData"
    5. :defaultExpandedKeys="['0-0-0', '0-0-1']"
    6. :defaultSelectedKeys="['0-0-0', '0-0-1']"
    7. :defaultCheckedKeys="['0-0-0', '0-0-1']"
    8. @select="this.onSelect"
    9. @check="this.onCheck"
    10. >
    11. <span slot="title0010" style="color: #1890ff">sss</span>
    12. </a-tree>
    13. </template>
    14. <script>
    15. const treeData = [
    16. {
    17. title: 'parent 1',
    18. key: '0-0',
    19. children: [
    20. {
    21. title: 'parent 1-0',
    22. key: '0-0-0',
    23. disabled: true,
    24. children: [
    25. { title: 'leaf', key: '0-0-0-0', disableCheckbox: true },
    26. { title: 'leaf', key: '0-0-0-1' },
    27. ],
    28. },
    29. {
    30. title: 'parent 1-1',
    31. key: '0-0-1',
    32. children: [{ key: '0-0-1-0', slots: { title: 'title0010' } }],
    33. },
    34. ],
    35. },
    36. ];
    37. export default {
    38. data() {
    39. return {
    40. treeData,
    41. };
    42. },
    43. methods: {
    44. onSelect(selectedKeys, info) {
    45. console.log('selected', selectedKeys, info);
    46. },
    47. onCheck(checkedKeys, info) {
    48. console.log('onCheck', checkedKeys, info);
    49. },
    50. },
    51. };
    52. </script>

    Tree 树形控件 - 图3

    自定义TreeNode字段

    替换treeNode中 title,key,children字段为treeData中对应的字段

    1. <template>
    2. <a-tree
    3. checkable
    4. :treeData="treeData"
    5. :defaultExpandedKeys="['0-0-0', '0-0-1']"
    6. :defaultSelectedKeys="['0-0-0', '0-0-1']"
    7. :defaultCheckedKeys="['0-0-0', '0-0-1']"
    8. @select="this.onSelect"
    9. @check="this.onCheck"
    10. :replaceFields="replaceFields"/>
    11. </template>
    12. <script>
    13. const treeData = [
    14. {
    15. name: 'parent 1',
    16. key: '0-0',
    17. child: [
    18. {
    19. name: '张晨成',
    20. key: '0-0-0',
    21. disabled: true,
    22. child: [
    23. { name: 'leaf', key: '0-0-0-0', disableCheckbox: true },
    24. { name: 'leaf', key: '0-0-0-1' },
    25. ],
    26. },
    27. {
    28. name: 'parent 1-1',
    29. key: '0-0-1',
    30. child: [{ key: '0-0-1-0', name:'zcvc' }],
    31. },
    32. ],
    33. },
    34. ];
    35. export default {
    36. data() {
    37. return {
    38. treeData,
    39. replaceFields:{
    40. children:'child',
    41. title:'name'
    42. }
    43. };
    44. },
    45. methods: {
    46. onSelect(selectedKeys, info) {
    47. console.log('selected', selectedKeys, info);
    48. },
    49. onCheck(checkedKeys, info) {
    50. console.log('onCheck', checkedKeys, info);
    51. },
    52. },
    53. };
    54. </script>

    Tree 树形控件 - 图4

    自定义图标

    可以针对不同的节点定制图标。

    <template>
      <a-tree :treeData="treeData" showIcon defaultExpandAll :defaultSelectedKeys="['0-0-0']">
        <a-icon type="down" slot="switcherIcon" />
        <a-icon slot="smile" type="smile-o" />
        <a-icon slot="meh" type="smile-o" />
        <template slot="custom" slot-scope="{selected}">
          <a-icon :type="selected ? 'frown':'frown-o'" />
        </template>
      </a-tree>
    </template>
    <script>
      const treeData = [
        {
          title: 'parent 1',
          key: '0-0',
          slots: {
            icon: 'smile',
          },
          children: [
            { title: 'leaf', key: '0-0-0', slots: { icon: 'meh' } },
            { title: 'leaf', key: '0-0-1', scopedSlots: { icon: 'custom' } },
          ],
        },
      ];
    
      export default {
        data() {
          return {
            treeData,
          };
        },
        methods: {
          onSelect(selectedKeys, info) {
            console.log('selected', selectedKeys, info);
          },
          onCheck(checkedKeys, info) {
            console.log('onCheck', checkedKeys, info);
          },
        },
      };
    </script>
    

    Tree 树形控件 - 图5

    拖动示例

    将节点拖拽到其他节点内部或前后。

    <template>
      <a-tree
        class="draggable-tree"
        :defaultExpandedKeys="expandedKeys"
        draggable
        @dragenter="onDragEnter"
        @drop="onDrop"
        :treeData="gData"
      />
    </template>
    
    <script>
      const x = 3;
      const y = 2;
      const z = 1;
      const gData = [];
    
      const generateData = (_level, _preKey, _tns) => {
        const preKey = _preKey || '0';
        const tns = _tns || gData;
    
        const children = [];
        for (let i = 0; i < x; i++) {
          const key = `${preKey}-${i}`;
          tns.push({ title: key, key });
          if (i < y) {
            children.push(key);
          }
        }
        if (_level < 0) {
          return tns;
        }
        const level = _level - 1;
        children.forEach((key, index) => {
          tns[index].children = [];
          return generateData(level, key, tns[index].children);
        });
      };
      generateData(z);
      export default {
        data() {
          return {
            gData,
            expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],
          };
        },
        methods: {
          onDragEnter(info) {
            console.log(info);
            // expandedKeys 需要受控时设置
            // this.expandedKeys = info.expandedKeys
          },
          onDrop(info) {
            console.log(info);
            const dropKey = info.node.eventKey;
            const dragKey = info.dragNode.eventKey;
            const dropPos = info.node.pos.split('-');
            const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
            const loop = (data, key, callback) => {
              data.forEach((item, index, arr) => {
                if (item.key === key) {
                  return callback(item, index, arr);
                }
                if (item.children) {
                  return loop(item.children, key, callback);
                }
              });
            };
            const data = [...this.gData];
    
            // Find dragObject
            let dragObj;
            loop(data, dragKey, (item, index, arr) => {
              arr.splice(index, 1);
              dragObj = item;
            });
            if (!info.dropToGap) {
              // Drop on the content
              loop(data, dropKey, item => {
                item.children = item.children || [];
                // where to insert 示例添加到尾部,可以是随意位置
                item.children.push(dragObj);
              });
            } else if (
              (info.node.children || []).length > 0 && // Has children
              info.node.expanded && // Is expanded
              dropPosition === 1 // On the bottom gap
            ) {
              loop(data, dropKey, item => {
                item.children = item.children || [];
                // where to insert 示例添加到尾部,可以是随意位置
                item.children.unshift(dragObj);
              });
            } else {
              let ar;
              let i;
              loop(data, dropKey, (item, index, arr) => {
                ar = arr;
                i = index;
              });
              if (dropPosition === -1) {
                ar.splice(i, 0, dragObj);
              } else {
                ar.splice(i + 1, 0, dragObj);
              }
            }
            this.gData = data;
          },
        },
      };
    </script>
    

    Tree 树形控件 - 图6

    异步数据加载

    点击展开节点,动态加载数据。

    <template>
      <a-tree :loadData="onLoadData" :treeData="treeData" />
    </template>
    
    <script>
      export default {
        data() {
          return {
            treeData: [
              { title: 'Expand to load', key: '0' },
              { title: 'Expand to load', key: '1' },
              { title: 'Tree Node', key: '2', isLeaf: true },
            ],
          };
        },
        methods: {
          onLoadData(treeNode) {
            return new Promise(resolve => {
              if (treeNode.dataRef.children) {
                resolve();
                return;
              }
              setTimeout(() => {
                treeNode.dataRef.children = [
                  { title: 'Child Node', key: `${treeNode.eventKey}-0` },
                  { title: 'Child Node', key: `${treeNode.eventKey}-1` },
                ];
                this.treeData = [...this.treeData];
                resolve();
              }, 1000);
            });
          },
        },
      };
    </script>
    

    Tree 树形控件 - 图7

    连接线

    带连接线的树。

    <template>
      <a-tree showLine :defaultExpandedKeys="['0-0-0']" @select="onSelect">
        <a-tree-node key="0-0">
          <span slot="title" style="color: #1890ff">parent 1</span>
          <a-tree-node title="parent 1-0" key="0-0-0">
            <a-tree-node title="leaf" key="0-0-0-0" />
            <a-tree-node title="leaf" key="0-0-0-1" />
            <a-tree-node title="leaf" key="0-0-0-2" />
          </a-tree-node>
          <a-tree-node title="parent 1-1" key="0-0-1">
            <a-tree-node title="leaf" key="0-0-1-0" />
          </a-tree-node>
          <a-tree-node title="parent 1-2" key="0-0-2">
            <a-tree-node title="leaf" key="0-0-2-0" />
            <a-tree-node title="leaf" key="0-0-2-1" />
          </a-tree-node>
        </a-tree-node>
      </a-tree>
    </template>
    
    <script>
      export default {
        methods: {
          onSelect(selectedKeys, info) {
            console.log('selected', selectedKeys, info);
          },
        },
      };
    </script>
    

    Tree 树形控件 - 图8

    可搜索

    可搜索的树。

    <template>
      <div>
        <a-input-search style="margin-bottom: 8px" placeholder="Search" @change="onChange" />
        <a-tree
          @expand="onExpand"
          :expandedKeys="expandedKeys"
          :autoExpandParent="autoExpandParent"
          :treeData="gData"
        >
          <template slot="title" slot-scope="{title}">
            <span v-if="title.indexOf(searchValue) > -1">
              {{title.substr(0, title.indexOf(searchValue))}}
              <span style="color: #f50">{{searchValue}}</span>
              {{title.substr(title.indexOf(searchValue) + searchValue.length)}}
            </span>
            <span v-else>{{title}}</span>
          </template>
        </a-tree>
      </div>
    </template>
    
    <script>
      const x = 3;
      const y = 2;
      const z = 1;
      const gData = [];
    
      const generateData = (_level, _preKey, _tns) => {
        const preKey = _preKey || '0';
        const tns = _tns || gData;
    
        const children = [];
        for (let i = 0; i < x; i++) {
          const key = `${preKey}-${i}`;
          tns.push({ title: key, key, scopedSlots: { title: 'title' } });
          if (i < y) {
            children.push(key);
          }
        }
        if (_level < 0) {
          return tns;
        }
        const level = _level - 1;
        children.forEach((key, index) => {
          tns[index].children = [];
          return generateData(level, key, tns[index].children);
        });
      };
      generateData(z);
    
      const dataList = [];
      const generateList = data => {
        for (let i = 0; i < data.length; i++) {
          const node = data[i];
          const key = node.key;
          dataList.push({ key, title: key });
          if (node.children) {
            generateList(node.children, node.key);
          }
        }
      };
      generateList(gData);
    
      const getParentKey = (key, tree) => {
        let parentKey;
        for (let i = 0; i < tree.length; i++) {
          const node = tree[i];
          if (node.children) {
            if (node.children.some(item => item.key === key)) {
              parentKey = node.key;
            } else if (getParentKey(key, node.children)) {
              parentKey = getParentKey(key, node.children);
            }
          }
        }
        return parentKey;
      };
      export default {
        data() {
          return {
            expandedKeys: [],
            searchValue: '',
            autoExpandParent: true,
            gData,
          };
        },
        methods: {
          onExpand(expandedKeys) {
            this.expandedKeys = expandedKeys;
            this.autoExpandParent = false;
          },
          onChange(e) {
            const value = e.target.value;
            const expandedKeys = dataList
              .map(item => {
                if (item.key.indexOf(value) > -1) {
                  return getParentKey(item.key, gData);
                }
                return null;
              })
              .filter((item, i, self) => item && self.indexOf(item) === i);
            Object.assign(this, {
              expandedKeys,
              searchValue: value,
              autoExpandParent: true,
            });
          },
        },
      };
    </script>
    

    Tree 树形控件 - 图9

    目录

    内置的目录树,multiple 模式支持 ctrl(Windows) / command(Mac) 复选。

    <template>
      <a-directory-tree multiple defaultExpandAll @select="onSelect" @expand="onExpand">
        <a-tree-node title="parent 0" key="0-0">
          <a-tree-node title="leaf 0-0" key="0-0-0" isLeaf />
          <a-tree-node title="leaf 0-1" key="0-0-1" isLeaf />
        </a-tree-node>
        <a-tree-node title="parent 1" key="0-1">
          <a-tree-node title="leaf 1-0" key="0-1-0" isLeaf />
          <a-tree-node title="leaf 1-1" key="0-1-1" isLeaf />
        </a-tree-node>
      </a-directory-tree>
    </template>
    <script>
      export default {
        methods: {
          onSelect(keys) {
            console.log('Trigger Select', keys);
          },
          onExpand() {
            console.log('Trigger Expand');
          },
        },
      };
    </script>
    

    API

    Tree props

    参数说明类型默认值
    treeData节点的配置描述,具体项见下表, 1.1.4 之前的版本使用treeNodesarray
    replaceFields替换treeNode中 title,key,children字段为treeData中对应的字段object{children:'children', title:'title', key:'key' }
    autoExpandParent是否自动展开父节点booleantrue
    checkable节点前添加 Checkbox 复选框booleanfalse
    checkedKeys(v-model)(受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点 key,则子节点自动选中;相应当子节点 key 都传入,父节点也自动选中。当设置checkablecheckStrictly,它是一个有checkedhalfChecked属性的对象,并且父子节点的选中与否不再关联string[] | number[] | {checked: string[] | number[], halfChecked: string[] | number[]}[]
    checkStrictlycheckable 状态下节点选择完全受控(父子节点选中状态不再关联)booleanfalse
    defaultCheckedKeys默认选中复选框的树节点string[] | number[][]
    defaultExpandAll默认展开所有树节点booleanfalse
    defaultExpandedKeys默认展开指定的树节点string[] | number[][]
    defaultExpandParent默认展开父节点booltrue
    defaultSelectedKeys默认选中的树节点string[] | number[][]
    disabled将树禁用boolfalse
    draggable设置节点可拖拽booleanfalse
    expandedKeys(.sync)(受控)展开指定的树节点string[] | number[][]
    filterTreeNode按需筛选树节点(高亮),返回 truefunction(node)-
    loadData异步加载数据function(node)-
    loadedKeys(受控)已经加载的节点,需要配合 loadData 使用string[] | number[][]
    multiple支持点选多个节点(节点本身)booleanfalse
    selectedKeys(.sync)(受控)设置选中的树节点string[] | number[]-
    showIcon是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式booleanfalse
    switcherIcon自定义树节点的展开/折叠图标slot-
    showLine是否展示连接线booleanfalse

    事件

    事件名称说明回调参数
    check点击复选框触发function(checkedKeys, e:{checked: bool, checkedNodes, node, event})
    dragenddragend 触发时调用function({event, node})
    dragenterdragenter 触发时调用function({event, node, expandedKeys})
    dragleavedragleave 触发时调用function({event, node})
    dragoverdragover 触发时调用function({event, node})
    dragstart开始拖拽时调用function({event, node})
    dropdrop 触发时调用function({event, node, dragNode, dragNodesKeys})
    expand展开/收起节点时触发function(expandedKeys, {expanded: bool, node})
    load节点加载完毕时触发function(loadedKeys, {event, node})
    rightClick响应右键点击function({event, node})
    select点击树节点触发function(selectedKeys, e:{selected: bool, selectedNodes, node, event})

    TreeNode props

    结点描述数据对象,是 treeNodes 中的一项,TreeNode 使用相同的 API。

    参数说明类型默认值
    class节点的 classstring-
    style节点的 stylestring|object-
    disableCheckbox禁掉 checkboxbooleanfalse
    disabled禁掉响应booleanfalse
    icon自定义图标。可接收组件,props 为当前节点 propsslot|slot-scope-
    isLeaf设置为叶子节点(设置了loadData时有效)booleanfalse
    key被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复!string | number内部计算出的节点位置
    selectable设置节点是否可被选中booleantrue
    title标题string|slot|slot-scope'—-'
    slots使用 treeNodes 时,可以通过该属性配置支持 slot 的属性,如 slots: { title: 'XXX'}object-
    scopedSlots使用 columns 时,可以通过该属性配置支持 slot-scope 的属性,如 scopedSlots: { title: 'XXX'}object-
    on事件对象,仅在 treeNodes 使用方式中生效,如{click: () => {}}object'—-'

    DirectoryTree props

    参数说明类型默认值
    expandAction目录展开逻辑,可选 false 'click' 'dblclick'stringclick

    FAQ

    在 showLine 时,如何隐藏子节点图标?

    文件图标通过 switcherIcon 来实现,如果不需要你可以覆盖对应的样式