Skip to main content

使用 Less 构建树结构视图

需求说明

  • 构建一颗无限层级的树结构视图
  • 节点之间使用连线表示父子关系

实现思路

基于 Html+Css 实现整个树结构视图展示 基于 Flex 布局实现树结构视图的排列(左右排列) 递归渲染树结构视图

  1. 使用React构建树结构视图
  2. 使用Less构建树结构视图样式

实现步骤

  1. 定义数据结构
const tree = [
{
id: 1,
name: "ROOT",
children: [
{
id: 2,
name: "MIDDLE",
children: [
{
id: 3,
name: "LEAF",
},
{
id: 4,
name: "LEAF",
},
],
},
{
id: 5,
name: "MIDDLE",
children: [
{
id: 6,
name: "LEAF",
},
{
id: 7,
name: "LEAF",
},
],
},
],
},
];
  1. 创建树结构视图组件
  • TagNode.tsx
import { Button } from "antd";
import classNames from "classnames";
import { NODE_TYPE } from "../../enums";
import "./index.less";

interface TagNodeProps {
nodeType: NODE_TYPE;
item: any;
add?: (values: any) => void;
children?: React.ReactNode;
}

/**
* @description TagNode component 标签树的基本节点
* 基本节点包括 根节点 ROOT、叶子节点LEAF、中间节点MIDDLE
* @returns {JSX.Element}
*/

const TagNode: React.FC<TagNodeProps> = (props) => {
const { nodeType, add, item, children } = props;

return (
<div
id={item.id}
className="tagNode"
draggable
onDrag={(e) => {
e.currentTarget.style.border = "dashed";
e.dataTransfer.setData("text", e.target.id);
}}
>
<div className={classNames("selfNode")}>
<Button
onClick={() => {
add?.({
parentId: item.id,
name: nodeType,
});
}}
>
{nodeType}
</Button>
</div>
{children && <div className="childNode">{children}</div>}
</div>
);
};

export default TagNode;
  • TagTree.tsx
import { Button } from "antd";
import TagNode from "./components/TagNode/TagNode";
import { NODE_TYPE } from "./enums";
import { useTagList } from "./hooks/useTagList";
import "./index.less";

const TagTree = () => {
const { tagTree, add, update, remove } = useTagList();

const renderTagTree = (values: any) => {
return values.map((tag: any) => {
return (
<TagNode
nodeType={tag.name as NODE_TYPE}
key={tag.id}
item={tag}
add={add}
>
{tag.children && renderTagTree(tag.children)}
</TagNode>
);
});
};

return (
<div className="tag_content">
<Button
onClick={() => {
add({
name: "ROOT",
});
}}
>
添加根节点
</Button>
{renderTagTree(tagTree)}
</div>
);
};

export default TagTree;
  1. 使用 Less 构建树结构视图样式
@line: {
@color: rgb(102, 0, 255);
@width: 10px;
@margin-left: 30px;
@size: 3px;
@radius: 5px;
};

@selfNode: {
@padding: 10px;
};

@leafNode: {
@padding: 10px 0;
};

.dragging {
background-color: black;
}

.tagNode {
display: flex;
align-items: center;
padding: 0 @selfNode[@padding];

.tagNode {
position: relative;
margin-left: @line[@margin-left];
&::before {
position: absolute;
left: -@line[@width];
top: 50%;
transform: translate(0, -50%);
content: "";
display: block;
width: @line[@width];
height: 100%;
border-left: @line[@size] solid @line[@color];
}
}

.tagNode:first-child {
&::before {
height: calc(50% + @line[@size]);
border-top: @line[@size] solid @line[@color];
border-top-left-radius: @line[@radius];
transform: translate(0, -(@line[@size] / 2));
}
}

.tagNode:last-child {
&::before {
height: calc(50% + @line[@size]);
border-bottom: @line[@size] solid @line[@color];
border-bottom-left-radius: @line[@radius];
transform: translate(0, calc(-100% + (@line[@size] / 2)));
}
}

.selfNode {
position: relative;
&::before {
position: absolute;
left: -@line[@width] - @selfNode[@padding] + @line[@size];
top: 50%;
transform: translate(0, -50%);
content: "";
display: block;
width: @line[@width] + @selfNode[@padding];
height: @line[@size];
background-color: @line[@color];
}

&::after {
position: absolute;
right: 0;
top: 50%;
transform: translate(100%, -50%);
content: "";
display: block;
width: @line[@width] + @selfNode[@padding];
height: @line[@size];
background-color: @line[@color];
}

&:only-child {
padding: @leafNode[@padding];
&::after {
display: none;
}
}
}

.tagNode:only-child {
&::before {
display: none;
}

& > .selfNode::before {
left: -@line[@width] - @selfNode[@padding];
}
}
}
  1. 效果展示

alt text