基于vue + element 封装一个基本的steps组件

1、新建m-step文件夹和index.js文件

import Step from '../m-steps/src/m-step';

/* istanbul ignore next */
Step.install = function(Vue) {
  Vue.component(Step.name, Step);
};

export default Step;

2、新建m-steps,src文件夹和index.js文件;以及m-step.vue和m-steps.vue文件

2.1、m-steps/src/m-steps.vue

<template>
  <div class="m-steps-area">
    <div class="m-steps">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  name: 'mSteps',
  props: {
    active: {
      type: Number,
      default: 0
    },
    finishStatus: {
      type: String,
      default: 'finish'
    },
    processStatus: {
      type: String,
      default: 'process'
    }
  },

  data() {
    return {
      steps: [],
    };
  },

  methods: {

  },

  watch: {
    active(newVal, oldVal) {
      this.$emit('change', newVal, oldVal);
    },

    steps(steps) {
      steps.forEach((child, index) => {
        child.index = index;
      });
    }
  }
};
</script>

<style lang="scss">

</style>

2.2、m-steps/src/m-step.vue

<template>
  <div class="m-steps-item">
    <div class="m-steps-icon" :class="['is-' + currentStatus]">
      <template v-if="currentActive <= index">
        <i class="el-icon-close" v-if="status == 'error'"></i>
        <span class="u-icon" v-else>{{ index + 1 }}</span>
      </template>
      <template v-else>
        <i class="el-icon-close" v-if="status == 'error'"></i>
        <i class="el-icon-check" v-else></i>
      </template>
    </div>
    <div class="m-steps-content" :class="['is-' + currentStatus]">
      <div class="u-steps-title">{{ title }}</div>
      <div class="u-steps-description">{{ description }}</div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'mStep',
  props: {
    title: String,
    icon: String,
    description: String,
    status: String
  },
  data() {
    return {
      index: "-1",
      currentActive: this.$parent.active,
      totalSteps: "",
      internalStatus: ''
    }
  },
  beforeCreate() {
    this.$parent.steps.push(this);
    this.totalSteps = this.$parent.steps;
    this.index = this.totalSteps.indexOf(this);
  },
  beforeDestroy() {
    const steps = this.$parent.steps;
    const index = steps.indexOf(this);
    if (index >= 0) {
      steps.splice(index, 1);
    }
  },

  computed: {
    currentStatus() {
      return this.status || this.internalStatus;
    },
  },

  methods: {
    updateStatus(val) {
      const prevChild = this.$parent.$children[this.index - 1];
      if (val > this.index) {
        this.internalStatus = this.$parent.finishStatus;
      } else if (val === this.index && this.prevStatus !== 'error') {
        this.internalStatus = this.$parent.processStatus;
      } else {
        this.internalStatus = 'wait';
      }

      // if (prevChild) prevChild.calcProgress(this.internalStatus);
    },
  },

  mounted() {
    const unwatch = this.$watch('index', val => {
      this.$watch('$parent.active', this.updateStatus, { immediate: true });
      this.$watch('$parent.processStatus', () => {
        const activeIndex = this.$parent.active;
        this.updateStatus(activeIndex);
      }, { immediate: true });
      unwatch();
    });
  }
}
</script>

<style lang="scss">
.m-steps-area {
  .m-steps {
    // padding: 30px 0;
    display: flex;

    .m-steps-item {
      display: inline-block;
      flex: 1; // 弹性盒模型对象的子元素都有相同的长度,且忽略它们内部的内容
      overflow: hidden;
      font-size: 16px;
      line-height: 32px;


      .m-steps-icon {
        display: inline-block;
        margin-right: 14px;
        margin-left: 14px;
        width: 32px;
        height: 32px;
        border-radius: 50%;
        text-align: center;
        border: 1px solid rgba(0, 0, 0, .4);
        color: rgba(0, 0, 0, 0.4);

        &.is-finish {
          color: #409EFF;
          border: 1px solid #409EFF;
        }

        &.is-process {
          border: 1px solid #409EFF;
          color: #ffffff;
          background-color: #409EFF;
        }

        &.is-error {
          border: 1px solid #F6384C;
          color: #F6384C;
        }
      }

      &:first-child {
        .m-steps-icon {
          margin-left: 0;
        }
      }

      &:last-child {
        flex: none;

        .u-steps-title {
          &::after {
            height: 0;
          }
        }
      }

      .m-steps-content {
        display: inline-block;
        vertical-align: top;
        color: rgba(0, 0, 0, 0.4);

        &.is-process,
        &.is-finish {
          color: #409EFF;
        }

        &.is-finish {
          .u-steps-title {
            &::after {
              background: #409EFF;
            }
          }
        }

        &.is-error {
          color: #F6384C;

          .u-steps-title {
            &::after {
              background: #F6384C;
            }
          }
        }

        .u-steps-title {
          position: relative;
          padding-right: 17px;
          font-weight: bold;
          display: inline-block;

          &::after {
            position: absolute;
            top: 16px;
            left: 100%;
            display: block;
            width: 9999px;
            height: 1px;
            background: #DCDCDC;
            content: "";
          }
        }

        .u-steps-description {
          font-size: 14px;
          max-width: 140px;
        }
      }
    }
  }
}
</style>

2.3、m-steps/index.js

import Steps from './src/m-steps';

/* istanbul ignore next */
Steps.install = function(Vue) {
  Vue.component(Steps.name, Steps);
};

export default Steps;

3、main.js文件引入

import mSteps from '@/components/m-steps'
import mStep from '@/components/m-step'
Vue.use(mStep)
Vue.use(mSteps)

4、使用

<mSteps :active="active" class="mb15" style="margin-top:60px;">
      <mStep title="填写项目信息"></mStep>
      <mStep title="上传申请材料"></mStep>
      <mStep title="支付保费"></mStep>
      <mStep title="金融机构审核"></mStep>
      <mStep title="出具保函"></mStep>
    </mSteps>
  </div>

 

posted @ 2022-05-17 09:28  wjs0509  阅读(453)  评论(0编辑  收藏  举报