diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx
new file mode 100644
index 00000000..040ea892
--- /dev/null
+++ b/libs/extension/src/components/VTree.tsx
@@ -0,0 +1,103 @@
+import { useState } from 'horizon';
+
+export interface IData {
+ id: string;
+ name: string;
+ indentation: number;
+ userKey: string;
+}
+
+type IItem = {
+ style: any,
+ hasChild: boolean,
+ onExpand: (id: string) => void,
+} & IData
+
+// TODO: 计算可以展示的最多数量,并且监听显示器高度变化修改数值
+const showNum = 50;
+const divHeight = 21;
+
+function Item({ name, style, userKey, hasChild, onExpand, id, indentation }: IItem) {
+ const key = userKey === '' ? '' : ` key = '${userKey}'`;
+ const showIcon = hasChild ? '△' : '';
+ const onClickExpand = () => {
+ onExpand(id);
+ }
+ return (
+
+
{showIcon}
+ {name + key}
+
+ )
+}
+
+function VTree({ data }: { data: IData[] }) {
+ const [scrollTop, setScrollTop] = useState(0);
+ const [collapseNode, setCollapseNode] = useState(new Set());
+ const changeExpandNode = (id: string) => {
+ const nodes = new Set();
+ collapseNode.forEach(value => {
+ nodes.add(value);
+ });
+ if (nodes.has(id)) {
+ nodes.delete(id);
+ } else {
+ nodes.add(id);
+ }
+ setCollapseNode(nodes);
+ };
+ const showList: any[] = [];
+
+ let totalHeight = 0;
+ let currentCollapseIndentation: null| number = null;
+ data.forEach((item, index) => {
+ // 存在未处理完的收起节点
+ if (currentCollapseIndentation !== null) {
+ const indentation = item.indentation;
+ // 缩进更大,不显示
+ if (indentation > currentCollapseIndentation) {
+ return;
+ } else {
+ // 缩进小,说明完成了该收起节点的子节点处理。
+ currentCollapseIndentation = null;
+ }
+ }
+ if (totalHeight >= scrollTop && showList.length <= showNum) {
+ const nextItem = data[index + 1];
+ const hasChild = nextItem ? nextItem.indentation > item.indentation : false;
+ showList.push(
+
+ )
+ }
+ totalHeight = totalHeight + divHeight;
+ let id = item.id;
+ if (collapseNode.has(id)) {
+ // 该节点需要收起子节点
+ currentCollapseIndentation = item.indentation;
+ }
+ });
+
+ const scroll = (event: any) => {
+ const scrollTop = event.target.scrollTop;
+ setScrollTop(Math.max(scrollTop - 100, 0));
+ }
+
+ return (
+
+ {showList}
+ {/* 确保有足够的高度 */}
+
+
+ )
+}
+
+export default VTree;