<template> 
 | 
  <view 
 | 
    class="tn-steps-class tn-steps" 
 | 
    :style="{ 
 | 
      flexDirection: direction 
 | 
    }" 
 | 
  > 
 | 
    <view 
 | 
      v-for="(item, index) in list" 
 | 
      :key="index" 
 | 
      class="tn-steps__item" 
 | 
      :class="[`tn-steps__item--${direction}`]" 
 | 
      @tap="clickStepItem(index)" 
 | 
    > 
 | 
      <!-- 数值模式 --> 
 | 
      <view 
 | 
        v-if="mode === 'number'" 
 | 
        class="tn-steps__item__number" 
 | 
        :style="{ 
 | 
          backgroundColor: currentIndex <= index ? 'transparent' : activeColor, 
 | 
          borderColor: currentIndex <= index ? inActiveColor : activeColor 
 | 
        }" 
 | 
      > 
 | 
        <text 
 | 
          class="tn-steps__item__number__text" 
 | 
          :class="[{'tn-steps__item__number__text--visible': currentIndex <= index}]" 
 | 
          :style="{ 
 | 
            color: currentIndex <= index ? inActiveColor : activeColor 
 | 
          }" 
 | 
        > 
 | 
          {{ index + 1}} 
 | 
        </text> 
 | 
        <view class="tn-steps__item__number__icon" :class="[`tn-icon-${item.icon || icon}`,{'tn-steps__item__number__icon--visible': currentIndex > index}]"></view> 
 | 
      </view> 
 | 
       
 | 
      <!-- 点模式 --> 
 | 
      <view 
 | 
        v-if="mode === 'dot'" 
 | 
        class="tn-steps__item__dot" 
 | 
        :style="{ 
 | 
          backgroundColor: currentIndex <= index ? inActiveColor : activeColor 
 | 
        }" 
 | 
      ></view> 
 | 
       
 | 
      <!-- 图标模式 --> 
 | 
      <view 
 | 
        v-if="mode === 'icon'" 
 | 
        class="tn-steps__item__icon" 
 | 
        :class="[iconModeClass(index)]" 
 | 
        :style="{ 
 | 
          color: currentIndex <= index ? inActiveColor : activeColor 
 | 
        }" 
 | 
      ></view> 
 | 
       
 | 
      <!-- 点图标模式 --> 
 | 
      <view v-if="mode === 'dotIcon'" class="tn-steps__item__dot-icon"> 
 | 
        <view v-if="currentIndex <= index" class="tn-steps__item__dot-icon--dot" :style="{backgroundColor: inActiveColor}"></view> 
 | 
        <view v-else class="tn-steps__item__dot-icon--icon" :class="[iconModeClass(index)]" :style="{color: activeColor}"></view> 
 | 
      </view> 
 | 
       
 | 
      <!-- 步骤下的文字 --> 
 | 
      <text 
 | 
        v-if="showTitle" 
 | 
        class="tn-steps__item__text tn-text-ellipsis" 
 | 
        :class="[`tn-steps__item__text--${direction}`]" 
 | 
        :style="{ 
 | 
          color: currentIndex <= index ? inActiveColor : activeColor 
 | 
        }" 
 | 
      > 
 | 
        {{ item.name }} 
 | 
      </text> 
 | 
       
 | 
      <!-- 连接的横线 --> 
 | 
      <view 
 | 
        v-if="index < list.length - 1" 
 | 
        class="tn-steps__item__line" 
 | 
        :class="[`tn-steps__item__line--${mode}`]" 
 | 
        :style="{ 
 | 
          borderColor: currentIndex <= index + 1 ? inActiveColor : activeColor 
 | 
        }" 
 | 
      ></view> 
 | 
    </view> 
 | 
  </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
  export default { 
 | 
    name: 'tn-steps', 
 | 
    props: { 
 | 
      // 模式类型 
 | 
      // dot -> 点 number -> 数字 icon -> 图标 dotIcon -> 点图标 
 | 
      mode: { 
 | 
        type: String, 
 | 
        default: 'dot' 
 | 
      }, 
 | 
      // 步骤条的方向 
 | 
      // row -> 横向 column -> 竖向 
 | 
      direction: { 
 | 
        type: String, 
 | 
        default: 'row' 
 | 
      }, 
 | 
      // 步骤条数据 
 | 
      list: { 
 | 
        type: Array, 
 | 
        default() { 
 | 
          return [] 
 | 
        } 
 | 
      }, 
 | 
      // 当前激活的步数 
 | 
      current: { 
 | 
        type: Number, 
 | 
        default: 0 
 | 
      }, 
 | 
      // 激活步骤的颜色 
 | 
      activeColor: { 
 | 
        type: String, 
 | 
        default: '#01BEFF' 
 | 
      }, 
 | 
      // 未激活步骤的颜色 
 | 
      inActiveColor: { 
 | 
        type: String, 
 | 
        default: '#AAAAAA' 
 | 
      }, 
 | 
      // 激活后显示的图标,在数字模式下有效 
 | 
      icon: { 
 | 
        type: String, 
 | 
        default: 'success' 
 | 
      }, 
 | 
      // 是否显示标题 
 | 
      showTitle: { 
 | 
        type: Boolean, 
 | 
        default: true 
 | 
      } 
 | 
    }, 
 | 
    computed: { 
 | 
      // icon模式下图标的值 
 | 
      iconModeClass() { 
 | 
        return (index) => { 
 | 
          const item = this.list[index] 
 | 
          // 状态被选中并且对应数据下存在selectIcon属性 
 | 
          if (this.currentIndex > index && item.hasOwnProperty('selectIcon')) { 
 | 
            return `tn-icon-${item.selectIcon}` 
 | 
          } else { 
 | 
            // 未选中 
 | 
            return `tn-icon-${item.icon || this.icon}` 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    }, 
 | 
    data() { 
 | 
      return { 
 | 
        currentIndex: 0 
 | 
      } 
 | 
    }, 
 | 
    watch: { 
 | 
      current: { 
 | 
        handler(val) { 
 | 
          this.currentIndex = val 
 | 
        }, 
 | 
        immediate: true 
 | 
      } 
 | 
    }, 
 | 
    methods: { 
 | 
      // 点击了某一个选项 
 | 
      clickStepItem(index) { 
 | 
        const chooseIndex = index + 1 
 | 
        this.currentIndex = chooseIndex 
 | 
        this.$emit('click', { index: chooseIndex }) 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
   
 | 
  $tn-steps-item-number-width: 44rpx; 
 | 
  $tn-steps-item-dot-width: 20rpx; 
 | 
   
 | 
  .tn-steps { 
 | 
    display: flex; 
 | 
    flex-direction: row; 
 | 
     
 | 
    &__item { 
 | 
      flex: 1; 
 | 
      position: relative; 
 | 
      display: flex; 
 | 
      align-items: center; 
 | 
      justify-content: center; 
 | 
      flex-direction: column; 
 | 
      min-width: 100rpx; 
 | 
      font-size: 28rpx; 
 | 
      text-align: center; 
 | 
       
 | 
      &__number { 
 | 
        // display: flex; 
 | 
        // flex-wrap: wrap; 
 | 
        // align-items: center; 
 | 
        // justify-content: center; 
 | 
        position: relative; 
 | 
        width: $tn-steps-item-number-width; 
 | 
        height: $tn-steps-item-number-width; 
 | 
        line-height: calc(#{$tn-steps-item-number-width} - 2rpx); 
 | 
        border: 1px solid #AAAAAA; 
 | 
        border-radius: 50%; 
 | 
        overflow: hidden; 
 | 
        transition: all 0.3s linear; 
 | 
         
 | 
        &__text { 
 | 
          position: absolute; 
 | 
          top: 0; 
 | 
          right: 0; 
 | 
          bottom: 0; 
 | 
          left: 0; 
 | 
          margin: auto; 
 | 
          transition: inherit; 
 | 
          transform: translateY(-#{$tn-steps-item-number-width}); 
 | 
           
 | 
          &--visible { 
 | 
            transform: translateY(0); 
 | 
          } 
 | 
        } 
 | 
         
 | 
        &__icon { 
 | 
          position: absolute; 
 | 
          top: 0; 
 | 
          right: 0; 
 | 
          bottom: 0; 
 | 
          left: 0; 
 | 
          margin: auto; 
 | 
          color: #FFFFFF; 
 | 
          transition: all 0.3s linear; 
 | 
          transform: translateY(#{$tn-steps-item-number-width}); 
 | 
           
 | 
          &--visible { 
 | 
            transform: translateY(0); 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__dot { 
 | 
        width: $tn-steps-item-dot-width; 
 | 
        height: $tn-steps-item-dot-width; 
 | 
        display: flex; 
 | 
        flex-direction: row; 
 | 
        border-radius: 50%; 
 | 
        transition: all 0.3s linear; 
 | 
         
 | 
        &--icon { 
 | 
          width: $tn-steps-item-number-width; 
 | 
          height: $tn-steps-item-number-width; 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__icon { 
 | 
        width: $tn-steps-item-number-width; 
 | 
        height: $tn-steps-item-number-width; 
 | 
        font-size: $tn-steps-item-number-width; 
 | 
        transition: all 0.3s linear; 
 | 
      } 
 | 
       
 | 
      &__dot-icon { 
 | 
        width: $tn-steps-item-number-width; 
 | 
        height: $tn-steps-item-number-width; 
 | 
        display: flex; 
 | 
        flex-direction: row; 
 | 
        align-items: center; 
 | 
        justify-content: center; 
 | 
        transition: all 0.3s linear; 
 | 
         
 | 
        &--dot { 
 | 
          width: $tn-steps-item-dot-width; 
 | 
          height: $tn-steps-item-dot-width; 
 | 
          border-radius: 50%; 
 | 
          transition: inherit; 
 | 
        } 
 | 
         
 | 
        &--icon { 
 | 
          width: $tn-steps-item-number-width; 
 | 
          height: $tn-steps-item-number-width; 
 | 
          font-size: $tn-steps-item-number-width; 
 | 
          transition: inherit; 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__text { 
 | 
        transition: all 0.3s linear; 
 | 
        &--row { 
 | 
          margin-top: 14rpx; 
 | 
        } 
 | 
         
 | 
        &--column { 
 | 
          margin-left: 14rpx; 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__line { 
 | 
        position: absolute; 
 | 
        z-index: 0; 
 | 
        vertical-align: middle; 
 | 
        transition: all 0.3s linear; 
 | 
      } 
 | 
       
 | 
      &--row { 
 | 
        display: flex; 
 | 
        flex-direction: column; 
 | 
         
 | 
        .tn-steps__item__line { 
 | 
          border-bottom-width: 1px; 
 | 
          border-bottom-style: solid; 
 | 
          width: 50%; 
 | 
          left: 75%; 
 | 
           
 | 
          &--dot { 
 | 
            top: calc(#{$tn-steps-item-dot-width} / 2); 
 | 
          } 
 | 
           
 | 
          &--number, &--icon, &--dotIcon { 
 | 
            top: calc(#{$tn-steps-item-number-width} / 2); 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &--column { 
 | 
        display: flex; 
 | 
        flex-direction: row; 
 | 
        justify-content: flex-start; 
 | 
        min-height: 120rpx; 
 | 
         
 | 
        .tn-steps__item__line { 
 | 
          border-left-width: 1px; 
 | 
          border-left-style: solid; 
 | 
          height: 50%; 
 | 
          top: 75%; 
 | 
           
 | 
          &--dot { 
 | 
            left: calc(#{$tn-steps-item-dot-width} / 2); 
 | 
          } 
 | 
           
 | 
          &--number, &--icon, &--dotIcon { 
 | 
            left: calc(#{$tn-steps-item-number-width} / 2); 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
</style> 
 |