背景
项目中需要一个进度条来展示处理的进度,要求如下:
1、需要进行分段,用来展示正常和异常状态;
2、异常状态需要提示信息
框架
vue3 + element-plus + TS
构思
一、进度条
在 div 中的 background 使用渐变属性 linear-gradient
来展示整个进度条的整体进度
二、异常状态
在进度条的 div 中添加新的 div 来展示异常进度,颜色可以自定义;最关键的是需要使用 margin-left:
属性来控制异常状态的进度
三、提示信息
这里使用了 element-plus
中的 popover
组件来实现
四、进度光标
实现方式跟异常状态
一样,只是需要不断的进行删除和添加
代码
typescript
<template>
<div class="step-total" :style="{ background: getProgress() }">
<el-popover v-for="block in abnormalBlock" trigger="hover" popper-class="popover-red" placement="bottom-start">
<template #reference>
<div :style = "`
width: ${block.width}px;
height: ${block.height}px;
background-color: ${block.color};
margin-left: ${block.marginLeft}px;
position: absolute;
border-radius: ${block.borderRadius}px;
`"/>
</template>
<div style="height: 100px">
<div>
<span style="font-size: 20px; color: #a4a4a4; align-items: center">
test
</span>
<span style="font-size: 20px; float: right; align-items: center; color: #ffffff">
{{block.id}}
</span>
</div>
<hr style="border: none; border-top: 1px solid rgba(255, 255, 255, 0.1)" />
<div>
<span style="font-size: 20px; color: #a4a4a4; align-items: center">
test
</span>
<span style="font-size: 20px; float: right; align-items: center; color: #ffffff">
30%
</span>
</div>
<hr style="border: none; border-top: 1px solid rgba(255, 255, 255, 0.1)" />
<div>
<span style="font-size: 20px; color: #a4a4a4; align-items: center">
test
</span>
<span style="font-size: 20px; float: right; align-items: center; color: #ffffff">
40%
</span>
</div>
</div>
</el-popover>
</div>
<div> 步数:{{currentPercent/stepPercent}}/百分比:{{ currentPercent }}% </div>
<div>
<el-button @click="addNormalByPercent"> 添加正常(百分比)</el-button>
<el-button @click="addAbnormalByPercent"> 添加异常(百分比)</el-button>
</div>
<div>
<el-button @click="addNormalByStep"> 添加正常(步数)</el-button>
<el-button @click="addAbnormalByStep"> 添加异常(步数)</el-button>
</div>
<div>
<el-button @click="addPoint"> 添加光标 </el-button>
<el-button @click="delPoint"> 删除光标 </el-button>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
//进度条总长度
const progressWidth = 1200;
//每个百分比的宽度
const percentWidth = progressWidth / 100;
//总共的 step 数
const totalStep = 80;
//当前步数
const currentStep = ref(0)
//当前 step 百分比
const currentPercent = ref(0)
//每个 step 的百分比
const stepPercent = 100 / totalStep;
//每个 step 的宽度
const stepWidth = progressWidth / totalStep;
//异常模块
const abnormalBlock = ref([]);
const getProgress = () => {
//分段式进度条样式
return `linear-gradient(to right, #528135 0%, #528135 ${ currentPercent.value }%,#ddd ${ currentPercent.value }%, #ddd 100%)`
}
//百分比添加
const addNormalByPercent = () => {
if(currentPercent.value == 100){
return
}
delPoint()
currentPercent.value ++;
addPoint()
}
//异常百分比添加
const addAbnormalByPercent = () => {
if(currentPercent.value == 100){
return
}
delPoint()
abnormalBlock.value.push({
id: currentPercent.value,
width: percentWidth, //百分比的宽度是(总长度/100)
height: 10,
color: "red",
marginLeft: currentPercent.value * percentWidth,
borderRadius: 0
})
currentPercent.value ++;
addPoint()
}
//步数添加
const addNormalByStep = () => {
if(currentPercent.value == 100){
return
}
delPoint()
currentStep.value ++;
//当前 step 的百分比,当前步数*每步的百分比
currentPercent.value = currentStep.value * stepPercent;
addPoint()
}
//异常步数添加
const addAbnormalByStep = () => {
if(currentPercent.value == 100){
return
}
delPoint()
abnormalBlock.value.push({
id: currentPercent.value,
width: stepWidth, //步数的宽度是每一步的宽度
height: 10,
color: "red",
marginLeft: currentStep.value * stepWidth,
borderRadius: 0
})
currentStep.value ++;
//当前 step 的百分比,当前步数*每步的百分比
currentPercent.value = currentStep.value * stepPercent;
addPoint()
}
//添加光标
const addPoint = () => {
if(currentPercent.value == 100 || currentPercent.value == 0){
return
}
abnormalBlock.value.push({
id: "锚点",
width: 3, //步数的宽度是每一步的宽度
height: 14,
color: "black",
marginLeft: currentPercent.value * percentWidth,
borderRadius: 2
})
}
//移除光标
const delPoint = () => {
const lastBlock = abnormalBlock.value[abnormalBlock.value.length - 1];
if(lastBlock && lastBlock.id === "锚点"){
abnormalBlock.value.pop();
}
}
</script>
<style lang="css">
.progress-div {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.step-total {
width: 1200px;
border-radius: 10px;
height: 10px;
align-items: center;
display: flex;
}
.el-popover.popover-red {
width: 224px !important;
box-sizing: border-box;
border-radius: 12px;
border: 1px solid;
border-bottom: 0;
border-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(236, 74, 41, 0.8), rgba(255, 255, 255, 0)) 1;
background: linear-gradient(
180deg,
rgba(255, 161, 161, 0.99) -43.868%,
rgba(161, 207, 255, 0.31) -27.981%,
rgba(161, 207, 255, 0.2) -8.684%,
rgba(162, 208, 255, 0.03) 25.187%,
rgba(162, 208, 255, 0) 143.869%
),
rgba(18, 18, 18, 0.6);
backdrop-filter: blur(2px);
}
.popover-red .el-popper__arrow {
display: none;
}
</style>