Vue实现一个“蛇形”步骤条
点击关注公众号,“技术干货”及时达!
「前言」
在现代Web应用中,步骤条(Step Progression)作为一种常见的UI组件,广泛应用于表单提交、任务进度以及多步骤操作等场景。为了提升用户体验,设计师往往会采用更加直观和有趣的方式展示步骤进度。今天,我们将通过 Vue 实现一个“蛇形”步骤图组件,这种独特的设计方式不仅能清晰地表达步骤的完成状态,还能给用户带来更加流畅和动态的交互体验。
前几天在做一个需求时就需要写一个步骤图组件,大致原型如下:
看到这个,内心无一点波澜,直接去项目配套 Ant Desin Vue 组件库中找对应组件拿来改改就 ojbk 了。
But,后端同事告诉我业务场景后发现还有操作空间,因为 UI 组件的 Step 步骤条不太符合当前业务,里面所有步骤都是连续的,但是当前业务场景是可以跳过其中一些步骤项,于是打算自己写一个,最后根据后端返回的数据来决定步骤项是否执行了。
customStep(V1)customStep.vue 完整代码:
template
divclass="custom-step"
divv-for="(item,index)instepList":key="index"class="step-item"
divclass="item-content"
divclass="step-title"@click="handleStepClick(index)"
divclass="step-num":class="{'step-num-finished':item.status==='finished'}"{{index+1}}
/div
divclass="setp-txt":class="{'step-txt-finished':item.status==='finished'}"{{item.title}}
/div
/div
divclass="split-line"v-if="!item.isLast":class="{'split-line-finished':isFinished(index)}"/div
/div
/div
/div
/template
scriptsetup
import{ref,computed}from'vue'
constprops=defineProps({
stepList:{
type:Array,
default:()=[]
}
})
//步骤列表
//conststepList=ref([
//{title:'确定意向',status:'finished',isLast:false},
//{title:'对接洽谈',status:'finished',isLast:false},
//{title:'项目报价',status:'unfinished',isLast:false},
//{title:'投标对比',status:'unfinished',isLast:false},
//{title:'合同拟定',status:'unfinished',isLast:false},
//{title:'转化完成',status:'finished',isLast:false},
//{title:'转化完成1',status:'finished',isLast:false},
//{title:'转化完成2',status:'finished',isLast:true},
//])
constisFinished=computed(()=index={
constprevStep=props.stepList[index];
constnextStep=props.stepList[index+1];
returnprevStep.status==='finished'nextStep.status==='finished';
})
constemit=defineEmits(['stepClick'])
consthandleStepClick=index={
emit('stepClick',index)
}
/script
stylelang="less"scoped
.custom-step{
display:flex;
align-items:center;
flex-wrap:wrap;
row-gap:20px;
width:100%;
padding:030px;
.step-item{
width:calc(100%/
.item-content{
box-sizing:border-box;
display:flex;
align-items:center;
}
.step-title{
width:80px;
text-align:center;
font-weight:
color:rgb(153,153,166);
cursor:pointer;
.step-num{
box-sizing:content-box;
width:35px;
margin:0auto;
line-height:35px;
font-size:16px;
border:3pxsolid#e3e8ec;
border-radius:100%;
}
.setp-txt{
margin-top:10px;
}
.step-num-finished{
color:rgb(26,188,156);
border:3pxsolidrgb(26,188,156);
}
.step-txt-finished{
color:rgb(26,188,156);
}
}
.split-line{
width:calc(100%-80px);
height:
margin-top:-25px;
background-color:#e3e8ec;
border-radius:
}
.split-line-finished{
background-color:rgb(26,188,156);
}
}
}
/style
最后效果如下:
页面初始渲染步骤,然后可以点击某个步骤项,进行业务操作后刷新页面重新渲染步骤条,这样可以根据业务需求跳过某些步骤项。
But,还有事,我这固定了一行显示6个步骤项,超过6个会换行,但是显示就有一点瑕疵,因为正常元素布局都是从左到右,所以超过6个步骤项就会这样显示:
1 - 2 - 3 - 4 - 5 - 6 -
7 - 8 - ...
产品给我说这样看着不太连贯,想实现一个类似 "S" 形的,看着会连贯一些,后面找了个例子改了改,算是符合当前需求了。
customStep(V2)customStep_.vue 完整代码:
template
divclass="container"
divv-for="(item,index)instepList"class="grid-item":key="index"
divclass="step":class="{'step-finished':item.status==='finished'}"@click="handleStepClick(index)"
{{item.title}}/div
/div
/div
/template
scriptsetup
import{ref}from'vue';
constprops=defineProps({
stepList:{
type:Array,
default:()=[]
}
})
//步骤列表
//conststepList=ref([
//{title:'确定意向',status:'finished'},
//{title:'对接洽谈',status:'finished'},
//{title:'项目报价',status:'unfinished'},
//{title:'投标对比',status:'unfinished'},
//{title:'合同拟定',status:'unfinished'},
//{title:'转化完成',status:'finished'},
//{title:'制定方案',status:'unfinished'},
//{title:'合同签订',status:'finished'},
//{title:'合同跟踪',status:'finished'},
//{title:'合同回款',status:'unfinished'},
//{title:'项目交付',status:'unfinished'},
//{title:'项目验收',status:'unfinished'},
//{title:'项目结束',status:'unfinished'},
//])
constemit=defineEmits(['stepClick'])
consthandleStepClick=index={
emit('stepClick',index)
}
/script
stylelang="less"scoped
@colNum://单行排列的步骤项个数(2、3、4、5、6、...)
@colEven:@colNum*//两行元素数
@lineWidth:35px;//步骤间连线长度
@rowDistance:50px;//行间距
@colDistance:@lineWidth;//列间距
@arrowSize://箭头大小
@stepColor:#9e9e9e;//步骤颜色
.container{
width:100%;
display:grid;
padding:30px
grid-template-columns:repeat(@colNum,1fr);
gap:@rowDistance@colDistance;
}
.grid-item{
position:relative;
display:flex;
align-items:center;
justify-content:center;
cursor:pointer;
&::before{
position:absolute;
content:
right:-@lineWidth;
width:@lineWidth;
height:
border-top:1pxdashed@stepColor;
}
&::after{
content:
position:absolute;
right:(-@colDistance/
transform:translateX(50%);
border-top:(@arrowSize/1.4)solidtransparent;
border-left:@arrowSizesolid@stepColor;
border-bottom:(@arrowSize/1.4)solidtransparent;
}
//给每行最后一个步骤(除最后一行)添加向下的连接箭头
&:nth-child(@{colNum}n){
&:not(:last-child){
.step{
&::before{
content:
position:absolute;
left:
bottom:-(@rowDistance/
height:@lineWidth;
border-left:1pxdashed@stepColor;
transform:translate(-50%,50%);
}
&::after{
content:
position:absolute;
left:
bottom:-(@rowDistance/
border-top:@arrowSizesolid@stepColor;
border-left:(@arrowSize/1.4)solidtransparent;
border-right:(@arrowSize/1.4)solidtransparent;
transform:translate(-50%,50%);
}
}
}
}
each(range(@colEven),{
&:nth-child(@{colEven}n+@{value}){
@isEvenLine:boolean(@value@colNum);
@modNum:mod(@value,@colEven);//余数1、2、3、4、5、0
/**偶数行旋转箭头,步骤倒序排列(使用transform交换位置)*/
when(@isEvenLine){
@transN:(@colNum+1+@colEven-@value-@value);
transform:translateX(calc(@transN*100%+@transN*@colDistance));
&::after{
transform:translateX(50%)rotate(180deg)!important;//旋转箭头
}
}
//最右排(nn+1位)隐藏多余的箭头(如果container设置了overflow:hidden则不用处理)
when(@modNum=@colNum),(@modNum=@colNum+1){
&::before,&::after{
display:none;
}
}
//最后一个步骤在奇数行需要隐藏连线箭头
whennot(@isEvenLine){
&:last-child{
&::before,&::after{
display:none;
}
}
}
}
})
}
.step{
position:relative;
width:100px;
line-height:40px;
font-size:16px;
text-align:center;
border-radius:
color:#9e9e9e;
border:2pxsolid#9e9e9e;
}
.step-finished{
background-color:#4caf50;
color:#fff;
border:2pxsolid#4caf50;
}
/style
两种对比效果:
大功告成!!!
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线