一、需求
步骤组件:项目中时常会遇到这种需求,某些数据需要前面的数据通过后才能输入的情况(即先要验证第一步才能进入第二步)——如:企业司机注册(先输入基本信息在验证驾驶证行驶证这些等);因此封装了步骤组件。
防抖按钮组件:项目一般像表单提交、待办审批、删除等等操作按钮,多次点击后会多次请求接口;为防止这种现象,因此封装了防抖按钮组件(一定时间内无论点击多少次,只有一次请求接口)
短信倒计时组件
二、最终效果
三、参数配置
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 | 每个步骤的具名 slot | String | - |
—icon | 步骤头 icon 展示(element 内置 icon) | String | - |
—description | 步骤头描述 | String | - |
—btnArr | 每个步骤的按钮 | Array | - |
----- btnTitle | 按钮文字信息 | String | - |
----- params | 每个按钮传参信息(可以随意定义字段) | String/Number | - |
----- fn | 按钮事件 | function | - |
isShowLastSuccess | 是否显示默认 icon 最后一步 | Boolean | true |
active | 设置当前激活步骤 | Number | 0 |
—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>
五、组件地址
六、相关文章
vue3+ts基于Element-plus再次封装基础组件文档