开发预览 更新于 2026-05-10

TreeView 树形列表

嵌套树形展示,支持单选 / 复选框(带级联)、展开 / 折叠、自定义图标。

基础用法

nodes 是一棵树(每个节点可有 children)。selectedKey + update:selectedKey 控制单选高亮;defaultExpandedKeys 设置初始展开的节点 key。disabled 节点不响应点击。

背景 视口
  • src
    • components
      • Home.vue
      • About.vue
      • Contact.vue
    • main.ts
    • styles.css
  • README.md
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeView, type TreeNode } from '@chufix-design/vue';

const selected = ref<string | null>('home');

const nodes: TreeNode[] = [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
          { key: 'contact', label: 'Contact.vue', disabled: true },
        ],
      },
      { key: 'main', label: 'main.ts' },
      { key: 'styles', label: 'styles.css' },
    ],
  },
  { key: 'readme', label: 'README.md' },
];
</script>
<template>
  <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px; max-width: 18rem;">
    <CfTreeView
      :nodes="nodes"
      :selected-key="selected"
      :default-expanded-keys="['src', 'components']"
      @update:selected-key="selected = $event"
    />
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfTreeView } from '@chufix-design/vue';

const selected = ref<string | null>('home');

const nodes= [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
          { key: 'contact', label: 'Contact.vue', disabled: true },
        ],
      },
      { key: 'main', label: 'main.ts' },
      { key: 'styles', label: 'styles.css' },
    ],
  },
  { key: 'readme', label: 'README.md' },
];
</script>
<template>
  <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px; max-width: 18rem;">
    <CfTreeView
      :nodes="nodes"
      :selected-key="selected"
      :default-expanded-keys="['src', 'components']"
      @update:selected-key="selected = $event"
    />
  </div>
</template>
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [selected, setSelected] = useState<string | null>('home');

  const nodes: TreeNode[] = [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
            { key: 'contact', label: 'Contact.vue', disabled: true },
          ],
        },
        { key: 'main', label: 'main.ts' },
        { key: 'styles', label: 'styles.css' },
      ],
    },
    { key: 'readme', label: 'README.md' },
  ];
  return (
    <>
      <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8, maxWidth: "18rem" }}>
          <CfTreeView nodes={nodes} selectedKey={selected} defaultExpandedKeys={['src', 'components']} onSelectedKeyChange={() => setSelected($event)}
          />
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [selected, setSelected] = useState<string | null>('home');

  const nodes= [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
            { key: 'contact', label: 'Contact.vue', disabled: true },
          ],
        },
        { key: 'main', label: 'main.ts' },
        { key: 'styles', label: 'styles.css' },
      ],
    },
    { key: 'readme', label: 'README.md' },
  ];
  return (
    <>
      <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8, maxWidth: "18rem" }}>
          <CfTreeView nodes={nodes} selectedKey={selected} defaultExpandedKeys={['src', 'components']} onSelectedKeyChange={() => setSelected($event)}
          />
        </div>
    </>
  );
}

复选框 + 级联

checkable 在每个节点前加复选框,配合 v-model (Vue) / value + onChange (React) 绑定 string[]cascade=true(默认)让父子勾选互相联动 — 勾父勾全部子节点,勾全部子节点反过来勾父;半选状态自动算 indeterminate

cascade=false 时各节点独立,常用于按行业 / 标签多选场景。

背景 视口
  • src
    • components
      • Home.vue
      • About.vue
    • main.ts
    • styles.css
  • README.md
  • src
    • components
      • Home.vue
      • About.vue
    • main.ts
    • styles.css
  • README.md
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeView, type TreeNode } from '@chufix-design/vue';

const checked = ref<string[]>(['styles']);

const nodes: TreeNode[] = [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
        ],
      },
      { key: 'main', label: 'main.ts' },
      { key: 'styles', label: 'styles.css' },
    ],
  },
  { key: 'readme', label: 'README.md' },
];
</script>
<template>
  <div style="display:grid; grid-template-columns: 1fr 1fr; gap: 16px;">
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView
        v-model="checked"
        :nodes="nodes"
        checkable
        :default-expanded-keys="['src', 'components']"
      />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView
        v-model="checked"
        :nodes="nodes"
        checkable
        :cascade="false"
        :default-expanded-keys="['src', 'components']"
      />
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfTreeView } from '@chufix-design/vue';

const checked = ref<string[]>(['styles']);

const nodes= [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
        ],
      },
      { key: 'main', label: 'main.ts' },
      { key: 'styles', label: 'styles.css' },
    ],
  },
  { key: 'readme', label: 'README.md' },
];
</script>
<template>
  <div style="display:grid; grid-template-columns: 1fr 1fr; gap: 16px;">
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView
        v-model="checked"
        :nodes="nodes"
        checkable
        :default-expanded-keys="['src', 'components']"
      />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView
        v-model="checked"
        :nodes="nodes"
        checkable
        :cascade="false"
        :default-expanded-keys="['src', 'components']"
      />
    </div>
  </div>
</template>
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [checked, setChecked] = useState<string[]>(['styles']);

  const nodes: TreeNode[] = [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
          ],
        },
        { key: 'main', label: 'main.ts' },
        { key: 'styles', label: 'styles.css' },
      ],
    },
    { key: 'readme', label: 'README.md' },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView value={checked} onChange={setChecked} nodes={nodes} checkable defaultExpandedKeys={['src', 'components']} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView value={checked} onChange={setChecked} nodes={nodes} checkable cascade={false} defaultExpandedKeys={['src', 'components']} />
          </div>
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [checked, setChecked] = useState<string[]>(['styles']);

  const nodes= [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
          ],
        },
        { key: 'main', label: 'main.ts' },
        { key: 'styles', label: 'styles.css' },
      ],
    },
    { key: 'readme', label: 'README.md' },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView value={checked} onChange={setChecked} nodes={nodes} checkable defaultExpandedKeys={['src', 'components']} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView value={checked} onChange={setChecked} nodes={nodes} checkable cascade={false} defaultExpandedKeys={['src', 'components']} />
          </div>
        </div>
    </>
  );
}

三档尺寸

size 控制节点行高与字号 — sm 紧凑(侧栏文件树常用)/ md 默认 / lg 大号触摸友好。

背景 视口
  • src
    • main.ts
    • App.vue
  • package.json
  • src
    • main.ts
    • App.vue
  • package.json
  • src
    • main.ts
    • App.vue
  • package.json
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeView, type TreeNode } from '@chufix-design/vue';

const a = ref<string | null>('main');
const b = ref<string | null>('main');
const c = ref<string | null>('main');

const nodes: TreeNode[] = [
  {
    key: 'src',
    label: 'src',
    children: [
      { key: 'main', label: 'main.ts' },
      { key: 'app', label: 'App.vue' },
    ],
  },
  { key: 'package', label: 'package.json' },
];
</script>
<template>
  <div style="display:grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px;">
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="sm" :nodes="nodes" :selected-key="a" :default-expanded-keys="['src']" @update:selected-key="a = $event" />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="md" :nodes="nodes" :selected-key="b" :default-expanded-keys="['src']" @update:selected-key="b = $event" />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="lg" :nodes="nodes" :selected-key="c" :default-expanded-keys="['src']" @update:selected-key="c = $event" />
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfTreeView } from '@chufix-design/vue';

const a = ref<string | null>('main');
const b = ref<string | null>('main');
const c = ref<string | null>('main');

const nodes= [
  {
    key: 'src',
    label: 'src',
    children: [
      { key: 'main', label: 'main.ts' },
      { key: 'app', label: 'App.vue' },
    ],
  },
  { key: 'package', label: 'package.json' },
];
</script>
<template>
  <div style="display:grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px;">
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="sm" :nodes="nodes" :selected-key="a" :default-expanded-keys="['src']" @update:selected-key="a = $event" />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="md" :nodes="nodes" :selected-key="b" :default-expanded-keys="['src']" @update:selected-key="b = $event" />
    </div>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
      <CfTreeView size="lg" :nodes="nodes" :selected-key="c" :default-expanded-keys="['src']" @update:selected-key="c = $event" />
    </div>
  </div>
</template>
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState<string | null>('main');
  const [b, setB] = useState<string | null>('main');
  const [c, setC] = useState<string | null>('main');

  const nodes: TreeNode[] = [
    {
      key: 'src',
      label: 'src',
      children: [
        { key: 'main', label: 'main.ts' },
        { key: 'app', label: 'App.vue' },
      ],
    },
    { key: 'package', label: 'package.json' },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16 }}>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="sm" nodes={nodes} selectedKey={a} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setA($event)} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="md" nodes={nodes} selectedKey={b} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setB($event)} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="lg" nodes={nodes} selectedKey={c} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setC($event)} />
          </div>
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState<string | null>('main');
  const [b, setB] = useState<string | null>('main');
  const [c, setC] = useState<string | null>('main');

  const nodes= [
    {
      key: 'src',
      label: 'src',
      children: [
        { key: 'main', label: 'main.ts' },
        { key: 'app', label: 'App.vue' },
      ],
    },
    { key: 'package', label: 'package.json' },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16 }}>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="sm" nodes={nodes} selectedKey={a} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setA($event)} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="md" nodes={nodes} selectedKey={b} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setB($event)} />
          </div>
          <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
            <CfTreeView size="lg" nodes={nodes} selectedKey={c} defaultExpandedKeys={['src']} onSelectedKeyChange={() => setC($event)} />
          </div>
        </div>
    </>
  );
}

隐藏连接线

showLine={false} 关掉父子之间的虚线 — 视觉更干净,常配合自定义 icon 使用。

背景 视口
  • src
    • components
      • Home.vue
      • About.vue
    • main.ts
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeView, type TreeNode } from '@chufix-design/vue';

const selected = ref<string | null>('home');

const nodes: TreeNode[] = [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
        ],
      },
      { key: 'main', label: 'main.ts' },
    ],
  },
];
</script>
<template>
  <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px; max-width: 18rem;">
    <CfTreeView
      :nodes="nodes"
      :selected-key="selected"
      :default-expanded-keys="['src', 'components']"
      :show-line="false"
      @update:selected-key="selected = $event"
    />
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfTreeView } from '@chufix-design/vue';

const selected = ref<string | null>('home');

const nodes= [
  {
    key: 'src',
    label: 'src',
    children: [
      {
        key: 'components',
        label: 'components',
        children: [
          { key: 'home', label: 'Home.vue' },
          { key: 'about', label: 'About.vue' },
        ],
      },
      { key: 'main', label: 'main.ts' },
    ],
  },
];
</script>
<template>
  <div style="border: 1px solid var(--line-1); border-radius: 8px; padding: 8px; max-width: 18rem;">
    <CfTreeView
      :nodes="nodes"
      :selected-key="selected"
      :default-expanded-keys="['src', 'components']"
      :show-line="false"
      @update:selected-key="selected = $event"
    />
  </div>
</template>
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [selected, setSelected] = useState<string | null>('home');

  const nodes: TreeNode[] = [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
          ],
        },
        { key: 'main', label: 'main.ts' },
      ],
    },
  ];
  return (
    <>
      <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8, maxWidth: "18rem" }}>
          <CfTreeView nodes={nodes} selectedKey={selected} defaultExpandedKeys={['src', 'components']} showLine={false} onSelectedKeyChange={() => setSelected($event)}
          />
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfTreeView } from '@chufix-design/react';

export default function Demo() {
  const [selected, setSelected] = useState<string | null>('home');

  const nodes= [
    {
      key: 'src',
      label: 'src',
      children: [
        {
          key: 'components',
          label: 'components',
          children: [
            { key: 'home', label: 'Home.vue' },
            { key: 'about', label: 'About.vue' },
          ],
        },
        { key: 'main', label: 'main.ts' },
      ],
    },
  ];
  return (
    <>
      <div style={{ border: "1px solid var(--line-1)", borderRadius: 8, padding: 8, maxWidth: "18rem" }}>
          <CfTreeView nodes={nodes} selectedKey={selected} defaultExpandedKeys={['src', 'components']} showLine={false} onSelectedKeyChange={() => setSelected($event)}
          />
        </div>
    </>
  );
}

API

Props

Prop类型默认值说明
nodesTreeNode[][]树形数据
modelValue (Vue) / value (React)string[][]已勾选的 key(仅 checkable)
expandedKeys / defaultExpandedKeysstring[]受控 / 非受控的展开列表
selectedKeystring | nullnull当前单选的 key
checkablebooleanfalse显示复选框
cascadebooleantrue父子勾选联动
selectable'single' | 'multiple'选择模式(仅影响交互,不影响 checkable)
size'sm' | 'md' | 'lg''md'字号 + 内距
showLinebooleantrue显示嵌套连接线
emptyTextstring | ReactNode'暂无数据'空状态文案

TreeNode

interface TreeNode {
  key: string;
  label: string;
  icon?: string;          // 原始 SVG 字符串,通过 v-html 注入;用 currentColor 描边可继承节点文字色
  children?: TreeNode[];
  disabled?: boolean;
  selectable?: boolean;   // 单独控制本节点是否可被单选高亮
  isLeaf?: boolean;       // 强制视为叶子节点(用于懒加载占位)
}

Events

Vue 事件React 回调载荷类型说明
update:modelValueonChangestring[]勾选项变化时触发,仅在 checkable=true 时有效
update:expandedKeysonExpandedKeysChangestring[]展开节点列表变化时触发
update:selectedKeyonSelectedKeyChangestring | null单选节点变化时触发
selectonSelectTreeNode点击节点(在可选状态下)时触发,载荷是原始 node
expandonExpand(node: TreeNode, expanded: boolean)节点展开 / 折叠时触发
checkonCheck(node: TreeNode, checked: boolean)节点勾选 / 取消时触发(仅 checkable=true

反馈与讨论

TreeView 树形列表 的讨论

0
0 / 600
正在加载评论...