vue 实现可以转弯的步骤条

会拐弯的步骤条效果如下

代码部分

这边我是自己封装了一个step.vue的组件,因为需要响应式,我这边需要兼容4:3和16:9的页面(vh,vw这些是封装了样式计算宽高完成响应式布局),所以加了一些宽度的判断

 1. 创建步骤条组件Steps.vue

<template>
  <div
    class="stepOut"
    :class="$store.getters.windowWidth <= 1024 ? 'stepOutSmall' : ''"
  >
    <ul>
      <li
        class="stepItem"
        v-for="(stepItem, index) in stepInfo.list"
        :key="index"
        :class="[
          $store.getters.windowWidth <= 1024
            ?  Math.floor(index / 3) % 2 == 0
              ? 'stepItemLeftStart'
              : 'stepItemBrother'
            :  Math.floor(index / 6) % 2 == 0
            ? 'stepItemLeftStart'
            : 'stepItemBrother',
          stepInfo.step == index ? 'stepItemProgress' : ''
        ]"
      >
        <!-- 模拟步骤条的节点,此处为数值,如果是完成的步骤则为对勾 Object.prototype.toString.call-->
        <div :class="stepInfo.step >= index + 1 ? 'icon active' : 'icon'">
          {{ stepInfo.step >= index + 1 ? '' : index + 1 }}
        </div>
        <!-- 模拟步骤条连接线,动态显示 一行最多展示6个,如果超过六个,连线则为圆弧状,如果是1024尺寸,则一行最多展示为3个
        如果此步骤能不能整除6/3或者此步骤index+1除以6/3的余数可以整除2的话,显示之前(前提:当从左往右的时,判断当前步骤的索引是否大于步骤的长度来决定显示,当为从右往左时,则判断当前索引是否大于步骤的长度来决定显示) -->
        <div
          v-if="
            $store.getters.windowWidth <= 1024
              ? (index + 1) % 3 != 0 || ((index + 1) / 3) % 2 == 0
              : (index + 1) % 6 != 0 || ((index + 1) / 6) % 2 == 0
          "
          :class="
            $store.getters.windowWidth <= 1024
              ? Math.floor(index / 3) % 2 == 0
                ? stepInfo.step >= index + 2
                  ? 'line lineActive'
                  : 'line'
                : stepInfo.step >= index + 1
                ? 'line lineActive'
                : 'line'
              : Math.floor(index / 6) % 2 == 0
              ? stepInfo.step >= index + 2
                ? 'line lineActive'
                : 'line'
              : stepInfo.step >= index + 1
              ? 'line lineActive'
              : 'line'
          "
          v-show="
            $store.getters.windowWidth <= 1024
              ? Math.floor(index / 3) % 2 == 0
                ? index + 1 !== stepInfo.list.length
                : index !== stepInfo.list.length
              : Math.floor(index / 6) % 2 == 0
              ? index + 1 !== stepInfo.list.length
              : index !== stepInfo.list.length
          "
        ></div>
        <!-- 圆弧线 
        如果可以整除6/3则为当前行的最后一个,显示为弧线 -->
        <div
          v-if="
            $store.getters.windowWidth <= 1024
              ? index != 0 && (index + 1) % 3 == 0
              : index != 0 && (index + 1) % 6 == 0
          "
          :class="[
            stepInfo.step >= index + 2
              ? 'lineCircle lineCircleActive'
              : 'lineCircle',
            $store.getters.windowWidth <= 1024
              ? ((index + 1) / 3) % 2 == 0
                ? 'lineCircleLeft'
                : ''
              : ((index + 1) / 6) % 2 == 0
              ? 'lineCircleLeft'
              : ''
          ]"
          v-show="index + 1 !== stepInfo.list.length"
        ></div>

        <!-- 步骤名称  -->
        <p class="stepStatus">{{ stepItem.title }}</p>
        <!-- 步骤描述  -->
        <p class="statusTime">{{ stepItem.description }}</p>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'steps',
  props: {
    // 传递步骤参数
    stepInfo: {
      type: Object,
      default: function () {
        return {
          list: [],
          step: 0
        }
      }
    }
  }
}
</script>
<style lang="scss" scoped>
.stepOut {
  width: 100%;
  height: vh(100 * 2);
  display: flex;
  justify-content: center;
  &.stepOutSmall {
    height: min_vh(100 * 2);
    .stepItem {
      width: min_vw(230 * 2);
      height: min_vh(100 * 2);
      float: left;
      font-family: 'PingFangSC-Regular';
      font-size: min_vw(14 * 2);
      text-align: center;
      position: relative;
      .lineCircle {
        left: 55%;
        top: min_vh(12 * 2);
        position: absolute;
        display: block;
        border-left-color: white;
        border-left-width: 0px;
        width: 67%;
        border-color: white white white white;
        border-left-style: hidden;
        border-left: none;
        border-top: min_vh(2 * 2) solid #c0c4cc;
        border-right: min_vh(2 * 2) solid #c0c4cc;
        border-bottom: min_vh(2 * 2) solid #c0c4cc;
        border-bottom: min_vh(2 * 2) solid #c0c4cc;
        border-left: 0px solid #c0c4cc;
        height: 102%;
        border-radius: 0px min_vw(25 * 2) min_vw(25 * 2) 0px;
      }
      &:nth-child(n + 6) {
        float: inherit !important;
        right: inherit;
      }
      &:nth-child(n + 3) {
        float: right !important;
        right: 8%;
      }
      &.stepItemLeftStart {
        float: left !important;
        right: 0;
        + .stepItemBrother {
          .line {
            display: none;
          }
        }
      }
      &.stepItemProgress {
        .icon {
          border: min_vh(2 * 2) solid rgb(48, 49, 51);
          color: rgb(48, 49, 51);
        }
        .stepStatus {
          color: #333;
        }
        .statusTime {
          color: #333;
          opacity: 0.8;
        }
      }
      .lineCircleActive {
        border-bottom: min_vh(2 * 2) solid #007cdb;
        border-top: min_vh(2 * 2) solid #007cdb;
        border-right: min_vh(2 * 2) solid #007cdb;
        border-left: 0px solid #007cdb;
      }
      .lineCircleLeft {
        left: 4%;
        width: 41%;
        border-top: min_vh(2 * 2) solid #c0c4cc;
        border-right: 0 solid #c0c4cc;
        border-bottom: min_vh(2 * 2) solid #c0c4cc;
        border-bottom: min_vh(2 * 2) solid #c0c4cc;
        border-left: min_vh(2 * 2) solid #c0c4cc;
        border-radius: min_vw(25 * 2) 0px 0px min_vw(25 * 2);
        &.lineCircleActive {
          border-bottom: min_vh(2 * 2) solid #007cdb;
          border-top: min_vh(2 * 2) solid #007cdb;
          border-right: 0 solid #007cdb;
          border-left: min_vh(2 * 2) solid #007cdb;
        }
      }

      .icon {
        width: min_vw(24 * 2);
        height: min_vw(24 * 2);
        border-radius: 50%;
        border: min_vh(2 * 2) solid #c0c4cc;
        margin: 0 auto;
        position: relative;
        z-index: 888;
        color: rgb(192, 196, 204);
      }
      .active {
        border: min_vh(2 * 2) solid #007cdb;
        color: #007cdb;
        &::after {
          content: '';
          background: url('~@/assets/img/rightCheck.png');
          width: min_vw(22 * 2);
          height: min_vw(22 * 2);
          background-size: contain;
          position: absolute;
          left: 0;
          top: 0;
          bottom: 0;
          right: 0;
          margin: auto;
        }
      }
      .line {
        position: absolute;
        top: min_vh(12 * 2);
        left: 54.5%;
        border-bottom: min_vh(2 * 2) solid #c0c4cc;
        width: min_vw(208 * 2);
        z-index: 111;
      }
      .lineActive {
        border-bottom: min_vh(2 * 2) solid #007cdb;
      }
      .stepStatus {
        color: rgba(87, 87, 87, 1);
        line-height: min_vh(36 * 2);
      }
      .statusTime {
        color: rgba(87, 87, 87, 1);
        opacity: 0.5;
      }
    }
  }
  .stepItem {
    width: vw(260 * 2);
    height: vh(100 * 2);
    float: left;
    font-family: 'PingFangSC-Regular';
    font-size: vw(14 * 2);
    text-align: center;
    position: relative;
    .lineCircle {
      left: 54%;
      top: vh(12 * 2);
      position: absolute;
      display: block;
      border-left-color: white;
      border-left-width: 0px;
      width: 51%;
      border-color: white white white white;
      border-left-style: hidden;
      border-left: none;
      border-top: vh(2 * 2) solid #c0c4cc;
      border-right: vh(2 * 2) solid #c0c4cc;
      border-bottom: vh(2 * 2) solid #c0c4cc;
      border-bottom: vh(2 * 2) solid #c0c4cc;
      border-left: 0px solid #c0c4cc;
      height: 102%;
      border-radius: 0px vw(25 * 2) vw(25 * 2) 0px;
    }
    &:nth-child(n + 6) {
      float: right !important;
      right: 2%;
    }
    &.stepItemLeftStart {
      float: left !important;
      right: 0;
      + .stepItemBrother {
        .line {
          display: none;
        }
      }
    }
    &.stepItemProgress {
      .icon {
        border: vh(2 * 2) solid rgb(48, 49, 51);
        color: rgb(48, 49, 51);
      }
      .stepStatus {
        color: #333;
      }
      .statusTime {
        color: #333;
        opacity: 0.8;
      }
    }
    .lineCircleActive {
      border-bottom: vh(2 * 2) solid #007cdb;
      border-top: vh(2 * 2) solid #007cdb;
      border-right: vh(2 * 2) solid #007cdb;
      border-left: 0px solid #007cdb;
    }
    .lineCircleLeft {
      left: 5%;
      width: 41%;
      border-top: vh(2 * 2) solid #c0c4cc;
      border-right: 0 solid #c0c4cc;
      border-bottom: vh(2 * 2) solid #c0c4cc;
      border-bottom: vh(2 * 2) solid #c0c4cc;
      border-left: vh(2 * 2) solid #c0c4cc;
      border-radius: vw(25 * 2) 0px 0px vw(25 * 2);
      &.lineCircleActive {
        border-bottom: vh(2 * 2) solid #007cdb;
        border-top: vh(2 * 2) solid #007cdb;
        border-right: 0 solid #007cdb;
        border-left: vh(2 * 2) solid #007cdb;
      }
    }

    .icon {
      width: vw(24 * 2);
      height: vw(24 * 2);
      border-radius: 50%;
      border: vh(2 * 2) solid #c0c4cc;
      margin: 0 auto;
      position: relative;
      z-index: 888;
      color: rgb(192, 196, 204);
    }
    .active {
      border: vh(2 * 2) solid #007cdb;
      color: #007cdb;
      &::after {
        content: '';
        background: url('~@/assets/img/rightCheck.png');
        width: vw(22 * 2);
        height: vw(22 * 2);
        background-size: contain;
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        right: 0;
        margin: auto;
      }
    }
    .line {
      position: absolute;
      top: vh(12 * 2);
      left: 54.5%;
      border-bottom: vh(2 * 2) solid #c0c4cc;
      width: vw(237 * 2);
      z-index: 111;
    }
    .lineActive {
      border-bottom: vh(2 * 2) solid #007cdb;
    }
    .stepStatus {
      color: rgba(87, 87, 87, 1);
      line-height: vh(36 * 2);
    }
    .statusTime {
      color: rgba(87, 87, 87, 1);
      opacity: 0.5;
    }
  }
}
</style>

 2. 在使用的页面引入Steps组件,并传入相关数据

<Steps :stepInfo="stepInfo"></Steps >
import Steps from '@/components/Steps/index';


components: {
    Steps
},

stepInfo: {
        list: [
          {
            status: '1',
            title: '步骤条1',
            description: '2021年7月7日'
          },
          {
            status: '2',
            title: '步骤条2',
            description: '2021年11月19日'
          },
          {
            status: '3',
            title: '步骤条3',
            description: '2021年12月31日'
          },
          {
            status: '4',
            title: '步骤条4',
            description: '2022年4月19日'
          },
          {
            status: '5',
            title: '步骤条5',
            description: '2022年5月1日'
          },
          {
            status: '6',
            title: '步骤条6',
            description: '2022年5月1日'
          },
          {
            status: '7',
            title: '步骤条7',
            description: '2022年7月1日'
          },
          {
            status: '8',
            title: '步骤条18',
            description: '2022年8月1日'
          },
          {
            status: '9',
            title: '步骤条19',
            description: '未完成'
          },
          {
            status: '1',
            title: '步骤条10',
            description: '2021年7月7日'
          },
          {
            status: '2',
            title: '步骤条11',
            description: '2021年11月19日'
          },
          {
            status: '3',
            title: '步骤条12',
            description: '2021年12月31日'
          },
          {
            status: '4',
            title: '步骤条13',
            description: '2022年4月19日'
          },
          {
            status: '5',
            title: '步骤条14',
            description: '2022年5月1日'
          },
          {
            status: '6',
            title: '步骤条15',
            description: '2022年5月1日'
          },
          {
            status: '7',
            title: '步骤条16',
            description: '2022年7月1日'
          },
          {
            status: '8',
            title: '步骤条17',
            description: '2022年8月1日'
          },
          {
            status: '9',
            title: '步骤条18',
            description: '未完成'
          }
        ],
        step: 14
      }
  • 注意下边style中的.stepOutSmall这个类名及其下边的可以全部删掉
      页面上样式可以将vw(16*2) 、vh(16*2)这种的改为16px;
      如果觉得改vw vh很麻烦可以使用下列函数放入你的文件中
    
    
    
    @function vw($px) {
      // 进行计算操作
      $result: ($px / 2);
      $factor: 100000;
      $roundedValue: round($result * $factor);
      $roundedResult: $roundedValue / $factor;
    
      @return #{$result}px;
    
    }
    @function vh($px) {
      $result: ($px / 2);
      $factor: 100000;
      $roundedValue: round($result * $factor);
      $roundedResult: $roundedValue / $factor;
      @return #{$result}px;
    }
    

    如果具体想查看vw函数使用方法,请看vue 响应式页面使用transform实现_vue 页面缩小事件-CSDN博客

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值