vue3+element-plus el-steps步骤、防抖按钮、倒计时组件二次封装(vue3项目)

一、需求

步骤组件:项目中时常会遇到这种需求,某些数据需要前面的数据通过后才能输入的情况(即先要验证第一步才能进入第二步)——如:企业司机注册(先输入基本信息在验证驾驶证行驶证这些等);因此封装了步骤组件。

防抖按钮组件:项目一般像表单提交、待办审批、删除等等操作按钮,多次点击后会多次请求接口;为防止这种现象,因此封装了防抖按钮组件(一定时间内无论点击多少次,只有一次请求接口)

短信倒计时组件

二、最终效果

在这里插入图片描述

三、参数配置

1、步骤组件代码示例

<t-step-wizard
  :stepData="stepData"
  :active="active"
  :successTitle="successTitle"
  @complete="complete"
>
  <template #first>第一步骤</template>
  .....
</t-step-wizard>

2、防抖按钮组件代码示例

<t-button :time="time" @click="exportExcel">导出</t-button>
<!-- time:防抖时间默认:1000毫秒 -->

3、短信倒计时组件代码示例

<t-timer-btn ref="TTimerBtn" :second="second" @click="sendCode" />
<!-- second:倒计时的时间默认:60秒 -->

4 、步骤组件 Attributes 继承 element-plus el-steps/el-step 提供的属性

参数说明类型默认值
stepData步骤数据源Array-
—id步骤 ID 唯一Number-
—title步骤头文字展示String-
—slotName每个步骤的具名 slotString-
—icon步骤头 icon 展示(element 内置 icon)String-
—description步骤头描述String-
—btnArr每个步骤的按钮Array-
----- btnTitle按钮文字信息String-
----- params每个按钮传参信息(可以随意定义字段)String/Number-
----- fn按钮事件function-
isShowLastSuccess是否显示默认 icon 最后一步Booleantrue
active设置当前激活步骤Number0
—lastBtnArr最后一步按钮需要多个Array-
----- btnTitle按钮文字信息String-
----- params每个按钮传参信息(可以随意定义字段)String/Number-
----- fn按钮事件function-
lastBtnTitle最后一步骤成功按钮文字String完成
successTitle最后一步骤成功提示语String-

5、步骤组件 事件

事件名说明返回值
complete最后一步按钮点击事件当前步骤值

四、完整代码

1、步骤组件代码示例

<template>
  <div class="step-wizard">
    <el-steps :active="active" simple finish-status="success" v-bind="$attrs">
      <el-step
        v-bind="$attrs"
        v-for="(item,index) in stepData"
        :key="index"
        :title="`${index+1} ${item.title}`"
        :icon="item.icon?item.icon:''"
        :description="item.description?item.description:''"
      ></el-step>
    </el-steps>
    <div class="content-step-main step-content">
      <div
        class="step-first flex-box flex-col flex-ver-h"
        v-for="(val,key) in stepContent()"
        :key="key"
        v-show="active===key"
      >
        <template v-if="val.slotName">
          <slot :name="val.slotName"></slot>
        </template>
        <fix-btn>
          <el-button
            v-for="(val1,key1) in val.btnArr"
            :key="key1"
            :type="val1.type||'danger'"
            @click="val1.fn(val1)"
            :disabled="val.disable||false"
          >{{val1.btnTitle}}</el-button>
        </fix-btn>
      </div>
      <div
        class="step-last flex-box flex-col flex-ver"
        v-if="active===stepData.length && isShowLastSuccess"
      >
        <div class="icon-success">
          <el-icon>
            <CircleCheck />
          </el-icon>
        </div>
        <h2 class="success-margin" v-html="successTitle"></h2>
        <fix-btn>
          <el-button
            v-if="!stepData[stepData.length-1].lastBtnArr"
            type="danger"
            @click="complete"
          >{{lastBtnTitle}}</el-button>
          <el-button
            v-else
            v-for="(item,key) in stepData[stepData.length-1].lastBtnArr"
            :key="key"
            :type="item.type||'danger'"
            @click="item.fn(item)"
            :disabled="item.disable||false"
          >{{item.btnTitle}}</el-button>
        </fix-btn>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
export default {
  name: 'TStepWizard',
}
</script>
<script setup lang="ts">
import FixBtn from './fixBtn.vue'
const props = defineProps({
  // 步骤数据
  stepData: {
    type: Array,
    default: () => {
      return []
    },
    required: true,
  },
  successTitle: {
    // 最后一步骤成功提示语
    type: String,
    default: '',
  },
  lastBtnTitle: {
    // 最后一步骤成功按钮文字
    type: String,
    default: '完成',
  },
  active: {
    type: Number,
    default: 0,
    required: true,
  },
  isShowLastSuccess: {
    type: Boolean,
    default: true,
  },
})
const stepContent = () => {
  return props.isShowLastSuccess
    ? props.stepData && props.stepData.slice(0, props.stepData.length - 1)
    : props.stepData
}
const emits = defineEmits(['complete'])
// 第四步完成
const complete = () => {
  emits('complete', props.active)
}
</script>
<style lang="scss">
.i_layout {
  .section {
    .layout-content {
      padding: 0;
    }
  }
}
.flex-box {
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
}
.flex-ver {
  align-items: center;
  justify-content: center;
}
.flex-col {
  flex-direction: column;
}
.step-wizard {
  position: relative;
  .el-steps--simple {
    border-radius: 0px;
  }
  .content-step-main {
    .step-last {
      .icon-success {
        color: #67c23a;
        font-size: 95px;
        margin-top: 40px;
      }
      .success-margin {
        margin-bottom: 70px;
      }
    }
  }
}
</style>

2、防抖按钮组件代码示例

<template>
  <el-button v-bind="$attrs" @click="handleClick">
    <slot />
  </el-button>
</template>
<script lang="ts">
export default {
  name: 'TButton',
}
</script>
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
  time: {
    type: Number,
    default: 1000,
  },
})
// 抛出事件
const emits = defineEmits(['click'])
const record = ref(0)
const handleClick = () => {
  let newTime = new Date()
  if (newTime.getTime() - record.value > props.time) {
    emits('click')
  }
  record.value = new Date().getTime()
}
</script>

3、短信倒计时组件代码示例

<template>
  <div class="timer-btn">
    <button
      class="sendSmsBtn"
      :class="{dissendSmsBtn:disabled,className}"
      type="button"
      @click="run"
      :disabled="disabled || time > 0"
    >{{ text }}</button>
  </div>
</template>

<script lang="ts">
export default {
  name: 'TTimerBtn',
}
</script>
<script setup lang="ts">
import { computed, watch, ref } from 'vue'
const props = defineProps({
  second: {
    type: Number,
    default: 60,
  },
  className: {
    type: String,
  },
})
const time = ref(0)
const disabled = ref(false)
const text = computed(() => {
  return time.value > 0 ? `${time.value}s 后重获取` : '获取验证码'
})
const emits = defineEmits(['click'])
const run = () => {
  emits('click')
  start()
}
// 重置倒计时0
const reset = () => {
  time.value = 0
}
const start = () => {
  time.value = props.second
  disabled.value = true
  timer()
}
const timer = () => {
  if (time.value > 0) {
    time.value--
    setTimeout(timer, 1000)
  } else {
    disabled.value = false
  }
}
// 暴露方法出去
defineExpose({ reset })
</script>
<style lang="scss" scoped>
.timer-btn {
  position: relative;
  .sendSmsBtn {
    height: 40px;
    line-height: 40px;
    border-radius: 4px;
    background: #ef473a;
    border: none;
    padding: 0 6px;
    color: #fff;
    display: inline-block;
    min-width: 92px;
    cursor: pointer;
  }
  .dissendSmsBtn {
    opacity: 0.5;
    cursor: auto;
  }
}
</style>

五、组件地址

gitHub组件地址

gitee码云组件地址

六、相关文章

基于ElementUi&antdUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档


vue2/3集成qiankun微前端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wocwin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值