본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

Tree 트리

원문 보기 위키 갱신

폴더나 조직도처럼 중첩된 계층 구조를 트리 형태로 보여 주는 컴포넌트예요.

출처: 문서

본문

언제 사용하나요?

폴더, 조직도, 생물 분류, 국가·지역 등 세상 대부분의 구조는 트리 구조예요. Tree 컴포넌트를 사용하면 이런 계층 관계를 온전하게 표현할 수 있고, 펼치기/접기, 선택 같은 인터랙션 기능도 제공해요.

기본 사용법

:::demo 중첩된 트리 구조 표현, 연결선, 펼치기/접기, 클릭 선택 기능을 보여 줘요. 애니메이션 효과는 트리 노드 높이에 영향을 받고 기본 높이는 30px이에요. 트리 노드를 커스텀할 때는 애니메이션이 정상적으로 표시되도록 노드 높이를 직접 설정하는 편이 좋아요.

<template>
  <d-tree :data="data"></d-tree>
</template>
<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'Parent node 1',
        children: [
          {
            label: 'Parent node 1-1',
            children: [{ label: 'Leaf node 1-1-1' }],
          },
          { label: 'Leaf node 1-2' },
        ],
      },
      { label: 'Leaf node 2' },
    ]);

    return {
      data,
    };
  },
});
</script>

:::

노드 지연 로딩

:::demo 해당 노드의 isLeaf 파라미터를 false로 설정하고, 컴포넌트가 lazyLoad 메서드를 콜백하도록 해서 노드 지연 로딩을 구현해요.

<template>
  <d-tree :data="data" @lazy-load="lazyLoad"></d-tree>
</template>
<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'Parent node 1',
        children: [
          {
            label: 'Parent node 1-1 - dynamic loading',
            isLeaf: false,
          },
          { label: 'Leaf node 1-2' },
        ],
      },
      { label: 'Leaf node 2 - dynamic loading', isLeaf: false },
    ]);

    const lazyLoad = (node, callback) => {
      setTimeout(() => {
        const data = [
          {
            label: 'child node 1',
            expanded: true,
            children: [
              {
                label: 'child node 1-1',
              },
              {
                label: 'child node 1-2',
              },
            ],
          },
          {
            label: 'child node 2',
            children: [
              {
                label: 'child node 2-1',
              },
            ],
          },
        ];
        callback({
          treeItems: data,
          node,
        });
      }, 500);
    };

    return {
      data,
      lazyLoad,
    };
  },
});
</script>

:::

체크 기능

:::demo check로 체크 기능을 켤 수 있어요.

<template>
  <div class="flex flex-row" style="height: 28px;">
    <label class="flex items-center mr-xl"><span class="inline-block mr-xs">开启勾选</span><d-switch v-model="openCheck"></d-switch></label>
    <d-radio-group v-if="openCheck" v-model="currentStrategy" direction="row">
      <d-radio v-for="strategy of checkStrategy" :key="strategy" :value="strategy">{{ strategy }}</d-radio>
    </d-radio-group>
  </div>
  <d-tree
    :data="data"
    :check="currentStrategy"
    @toggle-change="toggleChange"
    @check-change="checkChange"
    @select-change="selectChange"
    @node-click="nodeClick"
  ></d-tree>
</template>

<script>
import { defineComponent, ref, watch } from 'vue';

export default defineComponent({
  setup() {
    const openCheck = ref(true);
    const checkStrategy = ref(['both', 'downward', 'upward', 'none']);
    const currentStrategy = ref('both');

    const data = ref([
      {
        label: 'Parent node 1',
        children: [
          {
            label: 'Parent node 1-1',
            children: [{ label: 'Leaf node 1-1-1' }, { label: 'Leaf node 1-1-2' }],
          },
          { label: 'Leaf node 1-2' },
        ],
      },
      { label: 'Leaf node 2' },
    ]);

    watch(openCheck, (newVal) => {
      if (newVal === false) {
        currentStrategy.value = false;
      } else {
        currentStrategy.value = 'both';

        data.value = [
          {
            label: 'Parent node 1',
            children: [
              {
                label: 'Parent node 1-1',
                children: [{ label: 'Leaf node 1-1-1' }, { label: 'Leaf node 1-1-2' }],
              },
              { label: 'Leaf node 1-2' },
            ],
          },
          { label: 'Leaf node 2' },
        ];
      }
    });

    const toggleChange = (node) => {
      console.log('toggleChange node:', node);
    };

    const checkChange = (node) => {
      console.log('checkChange node:', node);
    };

    const selectChange = (node) => {
      console.log('selectChange node:', node);
    };

    const nodeClick = (node) => {
      console.log('nodeClick node:', node);
    };

    return {
      data,
      openCheck,
      checkStrategy,
      currentStrategy,
      toggleChange,
      checkChange,
      selectChange,
      nodeClick,
    };
  },
});
</script>

:::

기본 상태

:::demo expanded/selected/checked로 각각 기본 펼치기/접기 상태, 클릭 선택, 체크 상태를 설정해요.

<template>
  <d-tree :data="data" check></d-tree>
</template>

<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'Parent node 1',
        expanded: true,
        children: [
          {
            label: 'Parent node 1-1',
            children: [{ label: 'Leaf node 1-1-1' }],
          },
          {
            label: 'Leaf node 1-2',
            checked: true,
          },
        ],
      },
      {
        label: 'Leaf node 2',
        selected: true,
      },
    ]);

    return {
      data,
    };
  },
});
</script>

:::

비활성 상태

:::demo disableToggle/disableSelect/disableCheck로 각각 펼치기/접기, 클릭 선택, 체크를 비활성화해요.

<template>
  <d-tree :data="data" check></d-tree>
</template>

<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'Parent node 1',
        expanded: true,
        children: [
          {
            label: 'Parent node 1-1',
            disableToggle: true,
            disableSelect: true,
            disableCheck: true,
            children: [{ label: 'Leaf node 1-1-1' }],
          },
          {
            label: 'Leaf node 1-2',
            disableCheck: true,
          },
        ],
      },
      {
        label: 'Leaf node 2',
        disableSelect: true,
      },
    ]);

    return {
      data,
    };
  },
});
</script>

:::

커스텀 아이콘

:::demo content 슬롯으로 노드 내용을 커스텀할 수 있어요. 예를 들어 노드 내용 앞에 아이콘을 추가할 수 있어요. icon 슬롯으로는 펼치기/접기 아이콘을 커스텀할 수 있어요.

<template>
  <d-tree :data="data">
    <template #content="{ nodeData }">
      <svg style="margin-right: 8px" viewBox="0 0 16 16" width="16" height="16">
        <path
          :d="`${
            nodeData.isLeaf
              ? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
              : nodeData.expanded
              ? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
              : 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
          }`"
          stroke-width="1"
          fill="#8a8e99"
        ></path>
      </svg>
      {{ nodeData.label }}
    </template>
    <template #icon="{ nodeData, toggleNode }">
      <span v-if="nodeData.isLeaf" class="devui-tree-node__indent"></span>
      <span
        v-else
        @click="
          (event) => {
            event.stopPropagation();
            toggleNode(nodeData);
          }
        "
      >
        <svg
          :style="{ transform: nodeData.expanded ? 'rotate(90deg)' : '', marginLeft: '-2.5px', marginRight: '14.5px', cursor: 'pointer' }"
          viewBox="0 0 1024 1024"
          width="12"
          height="12"
        >
          <path d="M204.58705 951.162088 204.58705 72.836889 819.41295 511.998977Z" fill="#8a8e99"></path>
        </svg>
      </span>
    </template>
  </d-tree>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'Parent node 1',
        children: [
          {
            label: 'Parent node 1-1',
            children: [{ label: 'Leaf node 1-1-1' }],
          },
          { label: 'Leaf node 1-2' },
        ],
      },
      { label: 'Leaf node 2' },
    ]);

    return {
      data,
    };
  },
});
</script>

:::

노드 병합

:::demo

<template>
  <d-tree :data="data" ref="treeRef"></d-tree>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue';

export default defineComponent({
  setup() {
    const treeRef = ref(null);
    const data = ref([
      {
        label: 'Parent node 1',
        children: [
          {
            label: 'Parent node 1-1',
            children: [
              {
                label: 'Parent node 1-1-1',
                children: [
                  {
                    label: 'Parent node 1-1-1-1',
                  },
                ],
              },
            ],
          },
        ],
      },
      {
        label: 'Parent node 2',
        children: [
          {
            label: 'Parent node 2-1',
            children: [
              {
                label: 'Leaf node 2-1-1',
              },
              {
                label: 'Leaf node 2-1-2',
              },
            ],
          },
        ],
      },
      {
        label: 'Parent node 3',
        children: [
          {
            label: 'Leaf node 3-1',
            children: [
              {
                label: 'Leaf node 3-1-1',
                children: [
                  {
                    label: 'Leaf node 3-1-1-1',
                  },
                ],
              },
            ],
          },
          {
            label: 'Leaf node 3-2',
          },
        ],
      },
    ]);

    onMounted(() => {
      treeRef.value.treeFactory.mergeTreeNodes();
      treeRef.value.treeFactory.expandAllNodes();
    });

    return {
      data,
      treeRef,
    };
  },
});
</script>

:::

조작 버튼

:::demo 외부 조작 버튼과 호버 버튼을 정의할 수 있어요.

<template>
  <d-tree :data="data" ref="treeRef" operate @select-change="selectChange"></d-tree>
  <d-button variant="solid" size="sm" @click="addNode">Add</d-button>
  <d-button size="sm" class="ml-xs" @click="deleteNode">Delete</d-button>
</template>
<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const treeRef = ref();
    const selectedNode = ref();
    const data = ref([
      {
        label: 'Parent node 1',
        id: 'node-1',
        children: [
          {
            label: 'Leaf node 1-1',
            id: 'node-1-1',
            children: [{ label: 'Leaf node 1-1-1', id: 'node-1-1-1' }],
          },
          { label: 'Leaf node 1-2', id: 'node-1-2' },
        ],
      },
      { label: 'Parent node 2', id: 'node-2' },
    ]);

    const addNode = () => {
      if (!selectedNode.value) {
        return;
      }
      const childs = treeRef.value.treeFactory.getChildren(selectedNode.value);
      let labelName = 'new node';
      if (!childs || childs.length === 0) {
        labelName = selectedNode.value.label + '-1';
      } else if (childs.length > 0) {
        labelName = selectedNode.value.label + `-${childs.length + 1}`;
      }
      if (labelName.startsWith('Parent')) {
        labelName = labelName.replace('Parent', 'Leaf');
      }
      treeRef.value.treeFactory.insertBefore(selectedNode.value, { label: labelName });
    };

    const deleteNode = () => {
      if (!selectedNode.value) {
        return;
      }
      treeRef.value.treeFactory.removeNode(selectedNode.value);
    };

    const selectChange = (selected) => {
      selectedNode.value = selected;
    };

    return {
      treeRef,
      data,
      addNode,
      deleteNode,
      selectChange,
    };
  },
});
</script>

:::

검색과 필터

:::demo treeFactory의 searchTree 메서드로 노드를 검색하거나 필터링할 수 있어요.

<template>
  <d-search class="mb10" style="width: 300px" is-keyup-search placeholder="search your node..." :delay="1000" @search="onSearch"></d-search>
  <d-search
    class="mb10"
    style="width: 300px"
    is-keyup-search
    placeholder="filter your node..."
    :delay="1000"
    @search="onSearch1"
  ></d-search>
  <d-row class="mb10" align="middle">
    <d-search
      style="width: 300px"
      is-keyup-search
      placeholder="filter your node by custom key..."
      :delay="1000"
      @search="onSearch2"
    ></d-search>
    <span class="ml8">
      <d-tooltip position="top" content="使用自定义的属性搜索匹配树节点">
        <d-icon name="help" />
      </d-tooltip>
    </span>
  </d-row>
  <d-row class="mb10" align="middle">
    <d-search style="width: 300px" is-keyup-search placeholder="filter your node by Regex..." :delay="1000" @search="onSearch3"></d-search>
    <span class="ml8">
      <d-tooltip position="top" content="使用正则表达式限定搜索范围">
        <d-icon name="help" />
      </d-tooltip>
    </span>
  </d-row>
  <d-tree ref="treeRef" :data="data"></d-tree>
</template>

<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const treeRef = ref();
    const data = ref([
      {
        label: 'parent node 1',
        customSearchValue: 'a',
      },
      {
        label: 'parent node 2',
        customSearchValue: 'b',
        children: [
          {
            label: 'child node 2-1',
            customSearchValue: 'c',
            children: [
              {
                label: 'child node 2-1-1',
                customSearchValue: 'd',
              },
              {
                label: 'child node 2-1-2',
                customSearchValue: 'e',
              },
            ],
          },
          {
            label: 'child node 2-2',
            customSearchValue: 'f',
            children: [
              {
                label: 'child node 2-2-1',
                customSearchValue: 'g',
              },
              {
                label: 'child node 2-2-2',
                customSearchValue: 'h',
              },
            ],
          },
        ],
      },
      {
        label: 'parent node 3',
        customSearchValue: 'i',
        children: [
          {
            label: 'child node 3-1',
            customSearchValue: 'j',
          },
          {
            label: 'child node 3-2',
            customSearchValue: 'k',
          },
        ],
      },
      {
        label: 'parent node 4',
        customSearchValue: 'l',
        children: [
          {
            label: 'child node 4-1',
            customSearchValue: 'm',
          },
          {
            label: 'child node 4-2',
            customSearchValue: 'n',
          },
        ],
      },
      {
        label: 'parent node 5',
        customSearchValue: 'o',
        children: [
          {
            label: 'child node 5-1',
            customSearchValue: 'p',
          },
          {
            label: 'child node 5-2',
            customSearchValue: 'q',
          },
        ],
      },
    ]);
    const onSearch = (value) => {
      treeRef.value.treeFactory.searchTree(value, { isFilter: false });
    };
    const onSearch1 = (value) => {
      treeRef.value.treeFactory.searchTree(value, { isFilter: true });
    };
    const onSearch2 = (value) => {
      treeRef.value.treeFactory.searchTree(value, { isFilter: true, matchKey: 'customSearchValue' });
    };
    const onSearch3 = (value) => {
      const regex = new RegExp('^' + value + '[\s\S]*');
      treeRef.value.treeFactory.searchTree(value, { isFilter: true, pattern: regex });
    };

    return {
      treeRef,
      data,
      onSearch,
      onSearch1,
      onSearch2,
      onSearch3,
    };
  },
});
</script>
<style>
.mb10 {
  margin-bottom: 10px;
}
.ml8 {
  margin-left: 8px;
}
</style>

:::

가상 스크롤

:::demo 가상 스크롤로 대용량 데이터 로딩 문제를 처리해요.

<template>
  <d-search class="mb10" style="width: 300px" is-keyup-search placeholder="search your node..." :delay="1000" @search="onSearch"></d-search>
  <d-search
    class="mb10"
    style="width: 300px"
    is-keyup-search
    placeholder="filter your node..."
    :delay="1000"
    @search="onSearch1"
  ></d-search>
  <d-tree :data="data" :height="300" ref="treeRef"></d-tree>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue';

export default defineComponent({
  setup() {
    const treeRef = ref(null);
    const data = ref([
      ...Array.from({ length: 100 }).map((_, index) => ({
        label: 'Parent node ' + index,
        children:
          index % 2 === 0
            ? Array.from({ length: 10 }).map((_, index2) => ({
                label: 'Leaf node ' + index + '-' + index2,
              }))
            : undefined,
      })),
    ]);

    onMounted(() => {
      treeRef.value.treeFactory.expandAllNodes();
    });

    const onSearch = (value) => {
      treeRef.value.treeFactory.searchTree(value, { isFilter: false });
    };
    const onSearch1 = (value) => {
      treeRef.value.treeFactory.searchTree(value, { isFilter: true });
    };

    return {
      data,
      treeRef,
      onSearch,
      onSearch1,
    };
  },
});
</script>

:::

드래그 앤 드롭 트리

:::demo OperableTree의 dragdrop 속성으로 노드 드래그 기능을 설정하고, 외부 요소를 트리 안으로 드래그하는 것도 지원해요.

<template>
  <div class="mb-0">Default</div>
  <d-tree class="mb-2" :data="data" dragdrop></d-tree>
  <div class="mb-0">Sortable</div>
  <d-tree class="mb-2" :data="data" :dragdrop="{ dropPrev: true, dropNext: true, dropInner: true }"></d-tree>
</template>

<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const data = ref([
      {
        label: 'parent node 1',
      },
      {
        label: 'parent node 2',
        expanded: true,
        children: [
          {
            label: 'leaf node 2-1',
            expanded: true,
            children: [
              {
                label: 'leaf node 2-1-1',
              },
              {
                label: 'leaf node 2-1-2',
              },
            ],
          },
          {
            label: 'leaf node 2-2',
            children: [
              {
                label: 'leaf node 2-2-1',
              },
              {
                label: 'leaf node 2-2-2',
              },
            ],
          },
        ],
      },
      {
        label: 'parent node 3',
        expanded: true,
        children: [
          {
            label: 'leaf node 3-1',
          },
          {
            label: 'leaf node 3-2',
          },
        ],
      },
      {
        label: 'parent node 4',
        expanded: true,
        children: [
          {
            label: 'leaf node 4-1',
          },
          {
            label: 'leaf node 4-2',
          },
        ],
      },
      {
        label: 'parent node 5',
        expanded: true,
        children: [
          {
            label: 'leaf node 5-1',
          },
          {
            label: 'leaf node 5-2',
          },
        ],
      },
    ]);
    return {
      data,
    };
  },
});
</script>

:::

treeFactory 자주 쓰는 메서드

:::demo

<template>
  <d-tree :data="data" ref="treeRef"></d-tree>
  <d-button variant="solid" size="sm" @click="toggleNode">toggle parent node 1</d-button>
  <d-button variant="solid" size="sm" class="ml-xs" @click="activeNode">active parent node 1</d-button>
  <d-button variant="solid" size="sm" class="ml-xs" @click="addNode">add node for node 2</d-button>
  <d-button size="sm" class="ml-xs" @click="deleteNode">delete node 3</d-button>
</template>
<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const treeRef = ref();
    const data = ref([
      {
        label: 'Parent node 1',
        id: 'node-1',
        children: [
          {
            label: 'Leaf node 1-1',
            id: 'node-1-1',
            children: [{ label: 'Leaf node 1-1-1', id: 'node-1-1-1' }],
          },
          { label: 'Leaf node 1-2', id: 'node-1-2' },
        ],
      },
      { label: 'Node 2', id: 'node-2' },
      { label: 'Node 3', id: 'node-3' },
    ]);

    const toggleNode = () => {
      const treeData = treeRef.value.treeFactory.treeData.value;
      let node;
      treeData.forEach((item) => {
        if (item.id === 'node-1') {
          node = item;
        }
      });
      treeRef.value.treeFactory.toggleNode(node);
    };

    const activeNode = () => {
      const treeData = treeRef.value.treeFactory.treeData.value;
      let node;
      treeData.forEach((item) => {
        if (item.id === 'node-1') {
          node = item;
        }
      });
      treeRef.value.treeFactory.toggleSelectNode(node);
    };

    const addNode = () => {
      const treeData = treeRef.value.treeFactory.treeData.value;
      let node;
      treeData.forEach((item) => {
        if (item.id === 'node-2') {
          node = item;
        }
      });
      treeRef.value.treeFactory.insertBefore(node, { label: 'new node' });
    };

    const deleteNode = () => {
      const treeData = treeRef.value.treeFactory.treeData.value;
      let node;
      treeData.forEach((item) => {
        if (item.id === 'node-3') {
          node = item;
        }
      });
      if (!node) {
        return;
      }
      treeRef.value.treeFactory.removeNode(node);
    };

    return {
      treeRef,
      data,
      toggleNode,
      activeNode,
      addNode,
      deleteNode,
    };
  },
});
</script>

:::

Tree 파라미터

파라미터 타입 기본값 설명 Demo 링크
data ITreeNode[] [] 선택, 트리 구조 데이터 기본 사용법
check ICheck false 선택, 체크 기능 사용 여부 체크 가능
dragdrop IDragdrop false 선택, 드래그 기능 사용 여부 드래그 가능 트리

Tree 이벤트

이벤트명 콜백 파라미터 설명 Demo 링크
toggle-change Function(node) 노드 펼침/접힘 콜백 이벤트. 선택된 노드 객체 반환 체크 가능
check-change Function(node) 노드 체크 콜백 이벤트. 선택된 노드 객체 반환 체크 가능
select-change Function(node) 노드 선택 콜백 이벤트. 선택된 노드 객체 반환 체크 가능
node-click Function(node) 노드 클릭 이벤트. 클릭한 노드 객체 반환 체크 가능
lazy-node Function(node, callback) 노드 지연 로딩 이벤트. 클릭한 노드 객체와 콜백 함수 반환 노드 지연 로딩

Tree 슬롯

슬롯명 설명
default 노드 커스텀
content 노드 콘텐츠 커스텀
icon 펼침/접기 버튼 커스텀
loading 노드 지연 로딩 시 loading 표시 콘텐츠 커스텀

TreeNode 파라미터

파라미터 타입 기본값 설명
data ITreeNode [] 선택, 노드 데이터
check ICheck false 선택, 체크 기능 사용 여부
dragdrop IDragdrop false 선택, 드래그 기능 사용 여부
height number - 선택, 가상 스크롤 사용 설정

Tree 타입 정의

ITreeNode

interface ITreeNode {
  label: string;
  id?: string;
  children?: ITreeNode[];

  selected?: boolean;
  checked?: boolean;
  expanded?: boolean;

  disableSelect?: boolean;
  disableCheck?: boolean;
  disableToggle?: boolean;
}

ICheck

type ICheck = boolean | 'upward' | 'downward' | 'both' | 'none';

IDragdrop

type IDragdrop =
  | boolean
  | {
      dropPrev?: boolean;
      dropNext?: boolean;
      dropInner?: boolean;
    };

treeFactory

treeFactory는 컴포넌트 내부에서 노출하는 데이터와 메서드 모음으로, 트리 구조를 커스텀할 때 사용해요.

type TreeFactory = {
  // 扁平化处理后的数据,IInnerTreeNode 见下表
  treeData: Ref<IInnerTreeNode[]>;

  // 获取节点层级
  getLevel: (node: IInnerTreeNode) => number;

  // 获取某个节点的子节点
  getChildren: (
    node: IInnerTreeNode,
    config?: {
      expanded?: boolean; // 是否只从展开了的节点中获取数据
      recursive?: boolean; // 是否需要获取非直接子节点
    }
  ) => IInnerTreeNode[];

  // 获取某个节点的父节点
  getParent: (node: IInnerTreeNode) => IInnerTreeNode;

  // 获取当前是展开状态的节点
  getExpendedTree: () => ComputedRef<IInnerTreeNode[]>;

  // 获取某个节点在扁平化数据结构中的索引
  getIndex: (node: IInnerTreeNode) => number;

  // 设置某个节点的属性值
  setNodeValue: (node: IInnerTreeNode, key: keyof IInnerTreeNode, value: valueof<IInnerTreeNode>) => void;

  // 展开节点,并触发`toggle-change`事件
  expandNode: (node: IInnerTreeNode) => void;

  // 收起节点,并触发`toggle-change`事件
  collapseNode: (node: IInnerTreeNode) => void;

  // 展开/收起节点
  toggleNode: (node: IInnerTreeNode) => void;

  // 展开所有节点
  expandAllNodes: () => void;

  // 单选,并触发`select-change`事件
  selectNode: (node: IInnerTreeNode) => void;

  // 取消单选,并触发`select-change`事件
  deselectNode: (node: IInnerTreeNode) => void;

  // 单选/取消单选,并触发`select-change`事件
  toggleSelectNode: (node: IInnerTreeNode) => void;

  // 获取已单选的节点
  getSelectedNode: () => IInnerTreeNode;

  // 勾选,并触发`check-change`事件
  checkNode: (node: IInnerTreeNode) => void;

  // 取消勾选,并触发`check-change`事件
  uncheckNode: (node: IInnerTreeNode) => void;

  // 勾选/取消勾选,并触发`check-change`事件
  toggleCheckNode: (node: IInnerTreeNode) => void;

  // 获取已勾选的节点
  getCheckedNodes: () => IInnerTreeNode[];

  // 禁用节点单选
  disableSelectNode: (node: IInnerTreeNode) => void;

  // 禁用节点勾选
  disableCheckNode: (node: IInnerTreeNode) => void;

  // 禁用节点展开/收起
  disableToggleNode: (node: IInnerTreeNode) => void;

  // 启用节点单选
  enableSelectNode: (node: IInnerTreeNode) => void;

  // 启用节点勾选
  enableCheckNode: (node: IInnerTreeNode) => void;

  // 启用节点展开/收起
  enableToggleNode: (node: IInnerTreeNode) => void;

  // 当节点只有一个子节点时,合并显示
  mergeTreeNodes: () => void;

  // 懒加载某个节点的子节点
  lazyLoadNodes: (node: IInnerTreeNode) => void;

  // 搜索节点,参数为搜索关键字和搜索配置项,SearchFilterOption 见下表
  searchTree: (target: string, option: SearchFilterOption) => void;
};

IInnerTreeNode

interface IInnerTreeNode extends ITreeNode {
  level: number;
  idType?: 'random';
  parentId?: string;
  isLeaf?: boolean;
  parentChildNodeCount?: number;
  currentIndex?: number;
  loading?: boolean; // 节点是否显示加载中
  childNodeCount?: number; // 该节点的子节点的数量
  isMatched?: boolean; // 搜索过滤时是否匹配该节点
  childrenMatched?: boolean; // 搜索过滤时是否有子节点存在匹配
  isHide?: boolean; // 过滤后是否不显示该节点
  matchedText?: string; // 节点匹配的文字(需要高亮显示)
}

SearchFilterOption

interface SearchFilterOption {
  isFilter: boolean; // 是否是过滤节点
  matchKey?: string; // node节点中匹配搜索过滤的字段名
  pattern?: RegExp; // 搜索过滤时匹配的正则表达式
}

더 알아보기 (Learn more)

treeFactory가 제공하는 다양한 메서드를 활용하면 노드 추가·삭제·검색 같은 트리 조작을 자유롭게 확장할 수 있어요. 대용량 데이터를 다룰 때는 가상 스크롤 기능도 함께 살펴보세요.