树预览图

79 阅读2分钟

react中实现横向树图展示

image.png

代码

.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;