React封装组件:步骤条的实现和封装

发布于:2024-05-10 ⋅ 阅读:(25) ⋅ 点赞:(0)

前言

最近项目里需要用到步骤条的功能,但是ui给出的设计图我一看在antd上都没有这种样式的步骤条啊,所以得自己手动实现封装一个步骤条组件来使用,这是封装后的步骤条效果图:

image.png

实现思路:

首先我是使用了ul中的每个li,用浮动:float:left的方式将每个li向左浮动,我们将这个步骤条分成三大部分首(第一个li)、中(中间的li)、尾(最后一个li),第一个li我们可以拆解为一个长方形的div右边加上一个css实现的一个span三角形;最后一个的li跟第一个正好相反:一个长方形的div左边加上一个css实现的一个span三角形;中间部分的li就是结合在一起:一个div前后加上三角形就行了;

8111715218904_.pic.jpg 代码实现部分:

     <ul>
        {items?.map((item: StepObjType, index: number) => {
          const stepNumber = index + 1;
          let node = undefined;
          if (stepNumber == 1) {
            node = (
              <li key={item.key} style={{ width: `calc( 100% / ${items.length})` }}>
                <StepContent stepNumber={stepNumber} title={item?.title} />
                <span
                  className={classnames(styles.less, {
                    [styles.bgBorderColor]: current !== stepNumber,
                  })}
                ></span>
              </li>
            );
          } else if (index == items.length - 1) {
            node = (
              <li key={item.key} style={{ backgroundColor: current == stepNumber ? "#007eff" : "#f5f5f5f5", width: `calc( 100% / ${items.length})` }}>
                <StepAfterNode stepNumber={stepNumber} />
                <StepContent stepNumber={stepNumber} title={item?.title} />
              </li>
            );
          } else {
            node = (
              <li key={item.key} style={{ width: `calc( 100% / ${items.length})` }}>
                <StepAfterNode stepNumber={stepNumber} />
                <StepContent stepNumber={stepNumber} title={item?.title} />
                <span
                  className={classnames(styles.less, styles.right, {
                    [styles.bgBorderColor]: current !== stepNumber,
                  })}
                ></span>
              </li>
            );
          }
          return node;
        })}
        <div className={styles.clearfix}></div>
      </ul>

这里可以看到我是分成了三个部分然后进行了处理,我们还对步骤条进行数据封装,然后步骤条的数量就动态去items数组的长度就行了,我这里是分成了4个步骤:

type StepObjType = {
  key: string;
  title: string;
  content: React.ReactNode | string | undefined;
};

interface CustomStepsProps {
  current: number;
  items: StepObjType[];
}

import ProCard from "@ant-design/pro-card";
import CustomSteps from "../components/Steps";
import { useState } from "react";
import { Button, Space } from "antd";

const TestUiComponent: React.FC = () => {
  const [current, setCurrent] = useState(1);

  const stepsList = [
    {
      key: "first",
      title: "1、步骤1",
      content: "first",
    },
    {
      key: "seconde",
      title: "2、步骤2",
      content: "seconde",
    },
    {
      key: "3",
      title: "3、步骤3",
      content: "3333",
    },
    {
      key: "4",
      title: "4、步骤4",
      content: "44",
    },
  ];

  return (
    <ProCard>
      <CustomSteps current={current} items={stepsList} />
      <Space style={{marginTop:20}}>
        <Button
          type="primary"
          onClick={() => {
            setCurrent((pre) => pre - 1);
          }}
        >
          后退
        </Button>
        <Button
          type="primary"
          onClick={() => {
            setCurrent((pre) => pre + 1);
          }}
        >
          前进
        </Button>
      </Space>
    </ProCard>
  );
};

export default TestUiComponent;

我们也可以看一下三个步骤条效果:

WeChat1c222e0b3b09c48e8ad69bc901f4efe8.jpg

完整效果预览

步骤条.gif

完整代码

这里就两个文件不到200行的代码:css文件tsx文件

CustomSteps文件:

import classnames from "classnames";

import styles from "./index.less";

type StepObjType = {
  key: string;
  title: string;
  content: React.ReactNode | string | undefined;
};

interface CustomStepsProps {
  current: number;
  items: StepObjType[];
}

const CustomSteps: React.FC<CustomStepsProps> = ({ current = 1, items = [] }) => {
  // 步骤条内容
  const StepContent = ({ title, stepNumber }: { title: string; stepNumber: number }) => (
    <div
      className={classnames(styles.block, {
        [styles.bgColor]: current !== stepNumber,
        [styles.active]: current == stepNumber,
      })}
    >
      {title}
    </div>
  );

  // 这里只有最后步骤条或者中间的步骤条才有,第一个没有
  const StepAfterNode = ({ stepNumber }: { stepNumber: number }) => (
    <samp
      className={classnames(styles.less1, styles.left, {
        [styles.bgColor]: current !== stepNumber,
      })}
    ></samp>
  );

  return (
    <div className={styles.stepsPage}>
      <ul>
        {items?.map((item: StepObjType, index: number) => {
          const stepNumber = index + 1;
          let node = undefined;
          if (stepNumber == 1) {
            node = (
              <li key={item.key} style={{ width: `calc( 100% / ${items.length})` }}>
                <StepContent stepNumber={stepNumber} title={item?.title} />
                <span
                  className={classnames(styles.less, {
                    [styles.bgBorderColor]: current !== stepNumber,
                  })}
                ></span>
              </li>
            );
          } else if (index == items.length - 1) {
            node = (
              <li key={item.key} style={{ backgroundColor: current == stepNumber ? "#007eff" : "#f5f5f5f5", width: `calc( 100% / ${items.length})` }}>
                <StepAfterNode stepNumber={stepNumber} />
                <StepContent stepNumber={stepNumber} title={item?.title} />
              </li>
            );
          } else {
            node = (
              <li key={item.key} style={{ width: `calc( 100% / ${items.length})` }}>
                <StepAfterNode stepNumber={stepNumber} />
                <StepContent stepNumber={stepNumber} title={item?.title} />
                <span
                  className={classnames(styles.less, styles.right, {
                    [styles.bgBorderColor]: current !== stepNumber,
                  })}
                ></span>
              </li>
            );
          }
          return node;
        })}
        <div className={styles.clearfix}></div>
      </ul>
      {/* content */}
      <div className={styles.stepscontent}>{items[current - 1]?.content}</div>
    </div>
  );
};

export default CustomSteps;

css文件:

.stepsPage {
  .clearfix {
    clear: both;
  }
  ul {
    width: 100%;
    padding: 0;
    list-style: none;
  }
  li {
    position: relative;
    float: left;
    // width: 376px;
    // width: calc(100% / 4);
    height: 40px;
    margin-bottom: 10px;
    list-style: none;
    flex: 1;
  }
  .less {
    top: 0;
    border-color: transparent transparent transparent #007eff;
    border-style: dashed dashed dashed solid;
    border-width: 25px 0 15px 20px;
    transition: all 0.5;
  } /*dashed 设置透明*/
  .less1 {
    top: 0;
    border-color: transparent transparent transparent #fff;
    border-style: dashed dashed dashed solid;
    border-width: 25px 0 15px 20px;
  }
  .right {
    right: 0;
  }
  .left {
    left: 0;
  }
  samp {
    position: absolute;
    z-index: 2;
    display: block;
  }
  span {
    position: relative;
    z-index: 3;
    display: block;
    float: left;
  }
  .block {
    z-index: 1;
    float: left;
    // width: 357px;
    width: 92%;
    height: 40px;
    color: #222222;
    line-height: 40px;
    text-align: center;
    vertical-align: middle;
    background: #007eff;
  }
  .bgColor {
    background: #f5f5f5;
  }
  .bgBorderColor {
    border-color: transparent transparent transparent #f5f5f5;
  }
  .active {
    color: #fff;
  }

  .stepscontent {
    width: 100%;
    height: 200px;
    margin-top: 20px;
    background-color: #f7f7f7;
  }

  @media screen and (max-width: 1230px) {
     .block {
      width: 90%;
     }
  }
  @media screen and (max-width: 848px) {
    .block {
     width: 86%;
    }
 }
}

最后

这里封装的一个步骤条组件就实现啦,如果上面的实现有啥问题,欢迎各位大佬指点指点啊~~ , 感谢大家!


网站公告

今日签到

点亮在社区的每一天
去签到