树形数据

View source
通过 childrenKey 启用树形表格,配置展开、级联选择策略、缩进与选中结果读取。

树形数据基础

设置 children-key 即启用树形模式,配合 row-key 派生行 id、expand 列渲染折叠按钮。父级缺失字段(如职级)可由 emptyCell 占位:

成员部门岗位职级
李勇
产品
团队负责人
王超
市场
团队负责人
陈娜
设计
团队负责人
<script setup lang="ts">
import type { DataTableColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)

const columns: DataTableColumn<Person>[] = [
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' },
  { accessorKey: 'level', header: '职级', emptyCell: '' }
]
</script>

<template>
  <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" :ui="{ root: 'max-h-[50vh]' }" />
</template>

strategy 级联选择策略

selection 列的 strategy 决定父子勾选联动:cascade 父子级联、isolated 父子独立、leaf 仅叶子可勾(父节点展示为子孙派生态):

成员部门岗位
李勇
产品
团队负责人
王超
市场
团队负责人
陈娜
设计
团队负责人
<script setup lang="ts">
import type { DataTableColumn, DataTableSelectionColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const strategy = ref<DataTableSelectionColumn['strategy']>('cascade')

const columns = computed<DataTableColumn<Person>[]>(() => [
  { type: 'selection', strategy: strategy.value },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
])
</script>

<template>
  <div class="flex flex-col gap-3">
    <USelect
      v-model="strategy"
      :items="[
        { label: 'cascade 父子级联', value: 'cascade' },
        { label: 'isolated 父子独立', value: 'isolated' },
        { label: 'leaf 仅叶子可勾', value: 'leaf' }
      ]"
      value-key="value"
      size="xs"
      class="w-56"
    />
    <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" />
  </div>
</template>

mode 单选与多选

mode: 'single' 时整树仅单行勾选(通常配合 isolated);multiple 时配合 strategy 联动:

成员部门岗位
李勇
产品
团队负责人
王超
市场
团队负责人
陈娜
设计
团队负责人
<script setup lang="ts">
import type { DataTableColumn, DataTableSelectionColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const mode = ref<DataTableSelectionColumn['mode']>('multiple')

const columns = computed<DataTableColumn<Person>[]>(() => [
  { type: 'selection', mode: mode.value, strategy: mode.value === 'single' ? 'isolated' : 'cascade' },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
])
</script>

<template>
  <div class="flex flex-col gap-3">
    <USwitch
      :model-value="mode === 'single'"
      label="single 单选"
      @update:model-value="mode = $event ? 'single' : 'multiple'"
    />
    <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" />
  </div>
</template>

选中结果读取

v-model:row-selection-keys 双向同步选中 id 数组;组件 expose 的 treeSelection 派生 leavesparentshalfSelectedstrictlyChecked 四类业务视图,并提供 clearSelection()

成员部门
李勇
产品
王超
市场
陈娜
设计
{}
<script setup lang="ts">
import type { DataTableColumn, DataTableExposed } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const tableRef = useTemplateRef<DataTableExposed<Person>>('tableRef')
const selectionKeys = ref<string[]>([])
const names = (rows: Person[]) => rows.map(p => p.name)

const result = computed(() => {
  const r = tableRef.value?.treeSelection
  if (!r) return {}
  return {
    selectionKeys: selectionKeys.value,
    leaves: names(r.leaves),
    parents: names(r.parents),
    halfSelected: names(r.halfSelected),
    strictlyChecked: names(r.strictlyChecked)
  }
})

const columns: DataTableColumn<Person>[] = [
  { type: 'selection', strategy: 'cascade' },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' }
]
</script>

<template>
  <div class="flex flex-col gap-3">
    <UButton size="xs" variant="soft" class="self-start" @click="tableRef?.clearSelection()">
      清空选中
    </UButton>
    <MDataTable
      ref="tableRef"
      v-model:row-selection-keys="selectionKeys"
      :data="treeData"
      :columns="columns"
      children-key="children"
      row-key="id"
    />
    <pre class="text-xs p-3 rounded-md bg-muted overflow-auto">{{ result }}</pre>
  </div>
</template>

indentSize 树形缩进

indentSize 支持字符串、数字(px)与函数三种形态,函数形态可按 row.depth 等上下文动态调整:

成员部门岗位
李勇
产品
团队负责人
王超
市场
团队负责人
陈娜
设计
团队负责人
<script setup lang="ts">
import type { DataTableColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 3)

const columns: DataTableColumn<Person>[] = [
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
]
</script>

<template>
  <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" />
</template>

展开行为控制

expand 列的 buttonPropstoggleAllButtonProps 分别定制单元格与表头按钮(接收 isExpanded/isAllExpanded);expand-on-row-click 整行触发展开;expose 的 expandToDepth(n) 按层展开、collapseAll() 收起全部,v-model:expanded-keys 同步展开 id:

成员部门岗位
李勇
产品
团队负责人
王超
市场
团队负责人
陈娜
设计
团队负责人
<script setup lang="ts">
import type { DataTableColumn, DataTableExposed } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 3)
const tableRef = useTemplateRef<DataTableExposed<Person>>('tableRef')
const expandOnRowClick = ref(false)

const columns: DataTableColumn<Person>[] = [
  {
    type: 'expand',
    buttonProps: ctx => ({
      icon: ctx.isExpanded ? 'i-lucide-folder-open' : 'i-lucide-folder',
      color: ctx.isExpanded ? 'primary' : 'neutral',
      variant: 'soft'
    }),
    toggleAllButtonProps: ctx => ({
      icon: ctx.isAllExpanded ? 'i-lucide-chevrons-down-up' : 'i-lucide-chevrons-up-down',
      variant: 'soft',
      color: 'primary'
    })
  },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
]
</script>

<template>
  <div class="flex flex-col gap-3">
    <div class="flex flex-wrap items-center gap-2">
      <USwitch v-model="expandOnRowClick" label="expandOnRowClick" />
      <UButton size="xs" variant="soft" @click="tableRef?.expandToDepth(1)">
        展开 1 级
      </UButton>
      <UButton size="xs" variant="soft" @click="tableRef?.expandToDepth(2)">
        展开 2 级
      </UButton>
      <UButton size="xs" variant="soft" @click="tableRef?.collapseAll()">
        收起全部
      </UButton>
    </div>
    <MDataTable
      ref="tableRef"
      :data="treeData"
      :columns="columns"
      children-key="children"
      row-key="id"
      :expand-on-row-click="expandOnRowClick"
    />
  </div>
</template>
Copyright © 2025 - 2026 YiXuan - MIT License