react中实现横向树图展示
代码
.tree-gragh {
display: flex;
flex-direction: row;
.head {
display: flex;
align-items: center;
}
}
.nodeSingleWrap {
display: flex;
align-items: center;
}
.branchWrap {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.branch {
position: relative;
display: inline-flex;
align-items: center;
padding: 10px 0;
}
.leftTopHalfLine {
position: absolute;
width: 2px;
height: 50%;
top: 0px;
left: -1px;
background: #d9d9d9;
}
.leftBottomHalfLine {
position: absolute;
width: 2px;
height: 50%;
bottom: 0px;
left: -1px;
background: #d9d9d9;
}
.tree-node-wrap {
display: flex;
}
.tree-node-before {
position: relative;
padding: 0px 15px;
&::before {
content: "";
position: absolute;
width: calc(100% - 1px);
height: 2px;
margin: auto;
top: 0;
left: 0;
bottom: 0;
background: #d9d9d9;
}
}
.tree-node-after {
position: relative;
padding: 0px 15px;
&::before {
content: "";
position: absolute;
width: 100%;
height: 2px;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: #d9d9d9;
}
}
.tree-node-noTailLine {
&::before {
display: none;
}
}
.tree-node {
width: 200px;
position: relative;
background: #ffffff;
box-shadow: 0 2px 4px 0 #cccccc;
border-radius: 4px;
.tree-title {
height: 22px;
padding: 0 12px;
line-height: 22px;
color: #ffffff;
background: #007aff;
border-radius: 4px 4px 0 0;
}
.tree-content {
padding: 18px 14px;
line-height: 22px;
font-size: 14px;
color: #333333;
}
&::before {
content: "";
position: absolute;
top: 50%;
left: -10px;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
width: 0;
height: 4px;
border-style: solid;
border-width: 6px 1px 6px 8px;
border-color: transparent transparent transparent #cacaca;
background: #f5f5f7;
}
}
.tree-node-noHead {
&::before {
display: none;
}
}
import React from 'react';
import PropTypes from 'prop-types';
import styles from './index.module.less';
const Node = ({ nodeInfo, hasHead, hasTail }) => {
const { content, title } = nodeInfo || {};
return (
<div className={styles['tree-node-wrap']}>
{!!hasHead && <div className={styles['tree-node-before']} />}
<div className={`${styles['tree-node']} ${hasHead ? '' : styles['tree-node-noHead']}`}>
<div className={`${styles['tree-title']}`}>
{title}
</div>
<div className={styles['tree-content']}>
{content}
</div>
</div>
<div
className={`${styles['tree-node-after']} ${hasTail ? '' : styles['tree-node-noTailLine']}`}
/>
</div>
);
};
Node.propTypes = {
nodeInfo: PropTypes.objectOf(),
hasHead: PropTypes.bool,
hasTail: PropTypes.bool,
};
Node.defaultProps = {
nodeInfo: {},
hasHead: true,
hasTail: true,
};
export default Node;
import React from 'react';
import Node from './node';
import styles from './index.module.less';
const Tree = ({ treeData, childrenName = 'childrens' }) => {
const children = treeData[childrenName];
const renderChildren = (child) => {
const number = child?.length;
// 判断子元素是否存在
if (number) {
// 后代为单节点
if (number === 1) {
const node = child[0] || {};
const sonNode = node[childrenName];
return (
<div className={styles.nodeSingleWrap}>
<Node nodeInfo={node} hasTail={!!sonNode?.length} />
{sonNode?.length > 0 && renderChildren(sonNode)}
</div>
);
}
// 后代为多节点分支需要画连接线
return (
<div className={styles.branchWrap}>
{child.map((item, index) => {
const sonNode = item[childrenName];
return (
<div className={styles.branch}>
{index !== 0 && <div className={styles.leftTopHalfLine} />}
{index !== number - 1 && <div className={styles.leftBottomHalfLine} />}
<Node nodeInfo={item} hasTail={!!sonNode?.length} />
{sonNode?.length > 0 && renderChildren(sonNode)}
</div>
);
})}
</div>
);
}
return '';
};
return (
<div className={styles['tree-gragh']}>
<div className={styles.head}>
<Node nodeInfo={treeData} hasHead={false} />
</div>
<>{renderChildren(children)}</>
</div>
);
};
export default Tree;