Skip to content

Tree

用清晰的层级结构展示信息,可展开或折叠。

基础用法

基础的树形结构展示。

Level one 1
Level one 2
Level one 3

<template>
    <mc-tree class="vp-raw" :data="data" @expand="() => {}" />
</template>

<script lang="ts" setup>
import type { TreeOption } from 'mealcomes';

const data: TreeOption[] = [
    {
        label: 'Level one 1',
        key: '1',
        children: [
            {
                label: 'Level two 1-1',
                key: '11',
                children: [
                    {
                        label: 'Level three 1-1-1',
                        key: '111'
                    }
                ]
            }
        ]
    },
    {
        label: 'Level one 2',
        key: '2',
        children: [
            {
                label: 'Level two 2-1',
                key: '21',
                children: [
                    {
                        label: 'Level three 2-1-1',
                        key: '211'
                    }
                ]
            },
            {
                label: 'Level two 2-2',
                key: '22',
                children: [
                    {
                        label: 'Level three 2-2-1',
                        key: '221'
                    }
                ]
            }
        ]
    },
    {
        label: 'Level one 3',
        key: '3',
        children: [
            {
                label: 'Level two 3-1',
                key: '31',
                children: [
                    {
                        label: 'Level three 3-1-1',
                        key: '311'
                    }
                ]
            },
            {
                label: 'Level two 3-2',
                key: '32',
                children: [
                    {
                        label: 'Level three 3-2-1',
                        key: '321'
                    }
                ]
            }
        ]
    }
];
</script>

WARNING

传入的每个节点必须包含唯一的 key

节点选择

节点可选择,通过设置 multiple 可进行多选(ctrl + 左键),也可通过 show-checkbox 显示复选框,default-checked-keys 可控制默认选中的复选框。

Level one 1
Level one 2
Level one 3

<template>
    <div class="vp-raw">
        <div class="buttons" style="margin-bottom: 1rem">
            <mc-button @click="() => (multiple = !multiple)" size="small">
                {{ multiple ? '切换为单选' : '切换为多选' }}
            </mc-button>
            <mc-button
                @click="() => (showCheckbox = !showCheckbox)"
                size="small"
            >
                {{ showCheckbox ? '隐藏复选框' : '展示复选框' }}
            </mc-button>
        </div>
        <mc-tree
            :data="data"
            v-model:selected-keys="selectedKeys"
            :default-checked-keys="checkedKeys"
            selectable
            :show-checkbox="showCheckbox"
            :multiple="multiple"
            @select="handleSelect"
            @check="handleCheck"
        ></mc-tree>
    </div>
</template>

<script setup lang="ts">
import type { CheckedInfo, SelectInfo, TreeKey, TreeOption } from 'mealcomes';
import { ref } from 'vue';

const selectedKeys = ref<TreeKey[]>([]);
const checkedKeys = ref<TreeKey[]>(['11', '2', '31']);
const multiple = ref(false);
const showCheckbox = ref(false);

const data: TreeOption[] = [
    {
        label: 'Level one 1',
        key: '1',
        children: [
            {
                label: 'Level two 1-1',
                key: '11',
                children: [
                    {
                        label: 'Level three 1-1-1',
                        key: '111'
                    }
                ]
            }
        ]
    },
    {
        label: 'Level one 2',
        key: '2',
        children: [
            {
                label: 'Level two 2-1',
                key: '21',
                children: [
                    {
                        label: 'Level three 2-1-1',
                        key: '211'
                    }
                ]
            },
            {
                label: 'Level two 2-2',
                key: '22',
                children: [
                    {
                        label: 'Level three 2-2-1',
                        key: '221'
                    }
                ]
            }
        ]
    },
    {
        label: 'Level one 3',
        key: '3',
        children: [
            {
                label: 'Level two 3-1',
                key: '31',
                children: [
                    {
                        label: 'Level three 3-1-1',
                        key: '311'
                    }
                ]
            },
            {
                label: 'Level two 3-2',
                key: '32',
                children: [
                    {
                        label: 'Level three 3-2-1',
                        key: '321'
                    }
                ]
            }
        ]
    }
];

const handleSelect = (target: TreeOption, selectInfo: SelectInfo) => {
    console.log(target, selectInfo);
    target.isLeaf = true;
};

const handleCheck = (target: TreeOption, checkInfo: CheckedInfo) => {
    console.log(target, checkInfo);
    target.isLeaf = true;
};
</script>

节点展开控制

通过 v-model 双向绑定 expanded-keys 来控制节点的展开。

Level one 1
Level one 2
Level two 2-1
Level two 2-2
Level one 3
Level two 3-1
Level two 3-2

<template>
    <mc-tree
        class="vp-raw"
        v-model:expanded-keys="expandedKeys"
        :data="data"
        @expand="handleExpand"
    >
    </mc-tree>
</template>

<script setup lang="ts">
import { ExpandInfo, TreeKey, TreeOption } from 'mealcomes';
import { ref } from 'vue';

const expandedKeys = ref<TreeKey[]>([2, 3]);

const data = [
    {
        key: 1,
        label: 'Level one 1',
        children: [
            {
                key: 4,
                label: 'Level two 1-1',
                children: [
                    {
                        key: 9,
                        label: 'Level three 1-1-1'
                    },
                    {
                        key: 10,
                        label: 'Level three 1-1-2'
                    }
                ]
            }
        ]
    },
    {
        key: 2,
        label: 'Level one 2',
        children: [
            {
                key: 5,
                label: 'Level two 2-1'
            },
            {
                key: 6,
                label: 'Level two 2-2'
            }
        ]
    },
    {
        key: 3,
        label: 'Level one 3',
        children: [
            {
                key: 7,
                label: 'Level two 3-1'
            },
            {
                key: 8,
                label: 'Level two 3-2'
            }
        ]
    }
];

const handleExpand = (target: TreeOption, expandInfo: ExpandInfo) => {
    console.log(target, expandInfo);
};
</script>

懒加载

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

Out of Tao, One is bore
Out of Tao, One is bore

<template>
    <mc-tree
        class="vp-raw"
        :load="loadNode"
        :data="data"
        @loaded="handleLoaded"
    />
</template>

<script lang="ts" setup>
import type { TreeOption } from 'mealcomes';
import { ref } from 'vue';

const data = ref(createDataAsync());
let cnt = 4;

function createDataAsync(): TreeOption[] {
    return [
        {
            label: nextLabel(),
            key: '1',
            isLeaf: false
        },
        {
            label: nextLabel(),
            key: '2',
            isLeaf: false
        }
    ];
}

function nextLabel(currentLabel?: string | number | undefined): string {
    if (!currentLabel) return 'Out of Tao, One is bore';
    if (currentLabel === 'Out of Tao, One is bore') return 'Out of One, Two';
    if (currentLabel === 'Out of One, Two') return 'Out of two, Tree';
    if (currentLabel === 'Out of two, Tree')
        return 'Out of tree, the create universe';
    if (currentLabel === 'Out of tree, the create universe') {
        return 'Out of Tao, One is born';
    }
    return '';
}

const loadNode = (node: TreeOption) => {
    return new Promise<TreeOption[]>((res, rej) => {
        setTimeout(() => {
            if (cnt <= 0) rej();
            cnt--;
            res([
                {
                    label: nextLabel(node.label),
                    key: `${node.key}-1`,
                    isLeaf: false
                },
                {
                    label: nextLabel(node.label),
                    key: `${node.key}-2`,
                    isLeaf: false
                }
            ]);
        }, 1000);
    });
};

function handleLoaded(loadedData: TreeOption[]) {
    console.log(loadedData);
}
</script>

节点禁用

通过 disabled 设置节点的禁用状态。

Level one 1

<template>
    <mc-tree class="vp-raw" :data="data" show-checkbox />
</template>

<script lang="ts" setup>
const data = [
    {
        key: 1,
        label: 'Level one 1',
        children: [
            {
                key: 3,
                label: 'Level two 2-1',
                children: [
                    {
                        key: 4,
                        label: 'Level three 3-1-1'
                    },
                    {
                        key: 5,
                        label: 'Level three 3-1-2',
                        disabled: true
                    }
                ]
            },
            {
                key: 2,
                label: 'Level two 2-2',
                disabled: true,
                children: [
                    {
                        key: 6,
                        label: 'Level three 3-2-1'
                    },
                    {
                        key: 7,
                        label: 'Level three 3-2-2',
                        disabled: true
                    }
                ]
            }
        ]
    }
];
</script>

虚拟滚动

使用 height 属性则切换为虚拟滚动,从而实现大量数据的高性能树形展示。

node-1
node-2
node-3
node-4
node-5
node-6
node-7
node-8

<template>
    <mc-tree class="vp-raw" :data="data" :height="100"> </mc-tree>
</template>

<script setup lang="ts">
const getKey = (prefix: string, id: number) => {
    return `${prefix}-${id}`;
};

const createData = (
    maxDeep: number,
    maxChildren: number,
    minNodesNumber: number,
    deep = 1,
    key = 'node'
) => {
    let id = 0;
    return Array.from({ length: minNodesNumber })
        .fill(deep)
        .map(() => {
            const childrenNumber =
                deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren);
            const nodeKey = getKey(key, ++id);
            return {
                key: nodeKey,
                label: nodeKey,
                children: childrenNumber
                    ? createData(
                          maxDeep,
                          maxChildren,
                          childrenNumber,
                          deep + 1,
                          nodeKey
                      )
                    : undefined
            };
        });
};

const data = createData(4, 30, 40);
</script>

自定义节点内容

packages
docs

<template>
    <mc-tree class="vp-raw" :data="data" selectable>
        <template #default="{ node }">
            <mc-icon>
                <component v-if="!node.isLeaf" :is="Folder" />
                <component v-else :is="Document" />
            </mc-icon>
            <span class="custom-label">{{ node.label }}</span>
        </template>
    </mc-tree>
</template>

<script setup lang="ts">
import { Folder } from '@vicons/ionicons5';
import { Document } from '@vicons/ionicons5';

const data = [
    {
        label: 'packages',
        key: '1',
        children: [
            {
                label: 'src',
                key: '11',
                children: [
                    {
                        label: 'main.js',
                        key: '111',
                        isLeaf: true
                    },
                    {
                        label: 'App.vue',
                        key: '112',
                        isLeaf: true
                    }
                ]
            },
            {
                label: 'package.json',
                key: '12',
                isLeaf: true
            }
        ]
    },
    {
        label: 'docs',
        key: '2',
        children: [
            {
                label: 'README.md',
                key: '21',
                isLeaf: true
            }
        ]
    }
];
</script>

<style scoped>
.custom-label {
    margin-left: 6px;
    font-weight: 500;
}
</style>

API

Attributes

属性名说明类型默认值
data展示数据object-
labelField指定节点标签为节点对象的某个属性值stringlabel
keyField指定节点 key 为节点对象的某个属性值stringkey
childrenField指定子树为节点对象的某个属性值stringchildren
expandedKeys (v-model)展开的节点的key数组Array<string | number>-
selectable指定节点是否可选booleanfalse
multiple指定节点是否可以多选booleanfalse
selectedKeys (v-model)被选中的节点的 key 数组Array<string | number>-
showCheckbox节点展示 checkbox 复选框booleanfalse
defaultCheckedKeys默认 checkbox 被勾选的节点的 key 的数组Array<string | number>-
load数据懒加载(设置即代表启用懒加载树)(node) => Promise<TreeOption[]>>-
height设置虚拟滚动容器高度number-
item-size自定义树节点的高度(单位为像素)number27

Events

事件名说明类型
check点击复选框触发(target: TreeOption, checkInfo: CheckedInfo) => void
expand节点被展开或关闭时触发(target: TreeOption, expandInfo: ExpandInfo) => void
select节点被选择时触发(target: TreeOption, selectInfo: SelectInfo) => void
loaded异步加载成功时触发(loadedData: TreeOption[]) => void

Slots

插槽名说明类型
default自定义节点内容{ node }

Exposes

名称说明类型
getCheckedKeys获取当前 checkbox 选中节点的 key 数组(leafOnly?: boolean) => Array<string | number>
getCheckedNodes获取当前 checkbox 选中节点的数组(leafOnly?: boolean) => TreeOption[]
getHalfCheckedKeys获取当前 checkbox 半选节点的 key 数组() => Array<string | number>
getHalfCheckedNodes获取当前 checkbox 半选节点的数组() => TreeOption[]
getSelectedKeys获取当前选中节点的 key 数组() => Array<string | number>
getSelectedNodes获取当前选中节点的数组() => TreeOption[]
getExpandedKeys获取当前展开节点的 key 数组() => Array<string | number>
getExpandedNodes获取当前展开节点的数组() => TreeOption[]