微信小程序实现自定义steps进度条/步骤条组件

94 阅读3分钟

需求背景:用户参加某个活动,需要展示活动整体流程及用户当前操作在流程中的位置,支持左右滑动查看、自动定位到最新状态。

项目使用原生小程序开发,不使用第三方UI库。

样式参考如下图:

image.png

image.png

实现思路&代码如下:

新建一个components,使用时引入到页面中,或者直接在Page中写也可以。

  • wxml

由于需要支持左右滑动,以及自动定位到最新激活状态的节点,布局使用微信小程序的scroll-view组件可以很好实现,使用:

scroll-x 设置允许横向滑动

scroll-into-view 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素

enable-flex 开启flex布局

show-scrollbar 设置滚动条是否显示

每个节点包括图标/图片、文案、连接线,图标和连接线都有激活和非激活两种状态,对应两种样式。

我这里是根据 item.isActived 的值判断节点是否为激活态设置对应的样式

<scroll-view class="step-list" scroll-into-view="{{goStatus}}" scroll-x="{{true}}" enable-flex="{{true}}" enhanced show-scrollbar="false">
  <view wx:for="{{statusEnum}}" wx:key="status" id="{{item.status}}" class="step-con">
    <view wx:if="{{index > 0}}" class="{{item.isActived ? 'actived-line' : 'deactived-line'}} "></view>
    <view class="item">
      <view class="iconfont step-iconfont icon-{{item.isActived ? item.activedIcon :item.deActivedIcon}}"></view>
      <view class="step-tips {{item.isActived ? 'actived-tips' : 'deactived-tips'}}">{{item.value}}</view>
    </view>
  </view>
</scroll-view>
  • js

定义一个数组statusEnum存放所有进度条节点数据,数组中每一项包括:status、value、isActived、deActivedIcon、activedIcon,具体含义可以看代码注释部分。

定义变量goStatus标识要滚动到那个节点,默认值为第一个节点

定义方法 handelSteps 处理进度条数据

// js代码
data: {
    // 进度条数据初始化
    statusEnum: [{
        status: 'step1', // 进度条状态,一般需要与后端给的状态枚举值对齐
        value: '进度1', // 状态文案
        isActived: true, // 标记是否到达当前状态,即active激活态
        // 图标我用的iconfont,也可以不同状态使用不同图标/图片
        deActivedIcon: 'weixuanzhong', // 非active态iconfont图标,图片的话就写路径
        activedIcon: 'gouxuan', // active态图标
      },
      {
        status: 'step2',
        value: '进度2',
        isActived: false, // 未到达该状态
        deActivedIcon: 'weixuanzhong',
        activedIcon: 'gouxuan',
      },
      {
        status: 'step3',
        value: '进度3',
        isActived: false,
        deActivedIcon: 'weixuanzhong',
        activedIcon: 'gouxuan',
      },
      {
        status: 'step4',
        value: '进度4',
        isActived: false,
        deActivedIcon: 'weixuanzhong',
        activedIcon: 'gouxuan',
      },
      {
        status: 'step5',
        value: '进度5',
        isActived: false,
        deActivedIcon: 'weixuanzhong',
        activedIcon: 'gouxuan',
      },
      {
        status: 'step6',
        value: '进度6',
        isActived: false,
        deActivedIcon: 'weixuanzhong',
        activedIcon: 'gouxuan',
      },
    ],
    goStatus: 'step1', // 主动滚动到某个元素
  },
  methods: {
      // 处理进度条数据
      // 当前进度 一般会从接口获取到,将当前进度及之前的节点标记为active态
      // 比如传入step4,显示效果如图2
      handelSteps(step) {
      // 获取当前节点在数组中的位置
        const ind = this.data.statusEnum.findIndex(val => val.status === step)
        // 容错
        if (ind >= 0) {
          let data = JSON.parse(JSON.stringify(this.data.statusEnum))
          // ind及之前的节点标记为active
          for (let i = 0; i <= ind; i++) {
            data[i].isActived = true
          }
          const { status } = this.data.statusEnum[ind]
          this.setData({
            statusEnum: data,
            goStatus: status, // 主动滚动到当前最新active节点
          })
        }
      },
  }
  • wxss
/* scroll-view 样式 */
.step-list {
  display: flex;
  align-items: flex-start;
  flex-wrap: nowrap;
  background-color: aliceblue;
  padding: 32rpx 0;
  margin-top: 28rpx;
}
.step-con {
  font-size: 28rpx;
  display: flex;
}

/* ---- line样式 */
/* 非active态 */
.deactived-line {
  width: 40rpx;
  height: 0rpx;
  border: 2rpx dashed #DDDDDD;
  margin-top: 24rpx;
}
/* active态 */
.actived-line {
  width: 40rpx;
  height: 0rpx;
  border: 2rpx dashed #409EFF;
  margin-top: 24rpx;
}

 /* iconfont 样式 */
.step-iconfont {
  margin: 0rpx 28rpx;
  font-size: 48rpx;
  /* 非active */
  color: #999; 
}
/* active态 */
.icon-gouxuan {
  color: #409EFF;
}

/* 状态文案 */
.step-tips {
  height: 34rpx;
  font-size: 24rpx;
  line-height: 34rpx;
  font-weight: 400;
  text-align: right;
  padding-top: 12rpx;
  padding-right: 20rpx;
}

.actived-tips {
  color: #409EFF;
}
.deactived-tips {
  color: #999;
}