项目背景
ui设计师在设计线型进度条的时候在进度条底部加上了一个显示数据的面板,但是Element Plus提供的format和slot不满足此次的需求,就自己封装了一下。
代码
<template>
<div class="progress">
<el-progress :percentage="progressNumber" :show-text="showTextFn" />
<div ref="panel" v-show="showPlan" class="panel">{{ planTextStr }}</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, onMounted, watch } from 'vue';
const props = defineProps({
// 百分比,必填 0-100
percentage: {
type: Number,
default() {
return 0
}
},
// 进度条类型
type: {
type: String,
default() {
return 'line' // line/circle/dashboard
}
},
// stroke-width
strokeWidth: {
type: Number,
default() {
return 6
}
},
// 是否显示进度条文字内容
showText: {
type: Boolean,
default() {
return true
}
},
// 是否显示信息面板
showPlan: {
type: Boolean,
default() {
return false
}
},
// 面板显示文字
planText: String,
})
const progressNumber = computed(() => {
if (props.percentage > 100) return 100;
else if (props.percentage < 0) return 0;
else return props.percentage;
})
const showTextFn = computed(() => {
if (props.showPlan) return false
return props.showText
})
const panel = ref<any>(null);
const panelHeight = ref<string>('0px');
const translateX = ref<string>('0%');
const left = computed(() => `${progressNumber.value}%`);
const planTextStr = computed(() => {
if (props.planText) return props.planText;
else return `${progressNumber.value}%`
})
const showPlanType = computed(() => {
if (props.type === 'line') return props.showPlan;
else return false
})
onMounted(() => {
if (showPlanType.value) {
panelHeight.value = `${panel.value.offsetHeight + 5}px`;
const widthHalf = panel.value.offsetWidth / 2; // 面板宽度的一半
if (progressNumber.value > widthHalf && progressNumber.value < (100 - widthHalf)) translateX.value = '-50%';
else translateX.value = '0%';
}
})
// 监听百分比变化 修改面板translateX
watch(
progressNumber,
(val, preVal) => {
if (!showPlanType.value) return
const widthHalf = panel.value.offsetWidth / 2; // 面板宽度的一半
if (progressNumber.value > widthHalf && progressNumber.value < (100 - widthHalf)) translateX.value = '-50%';
else if (progressNumber.value >= (100 - widthHalf)) translateX.value = '-100%';
else translateX.value = '0%';
}
)
</script>
.progress {
position: relative;
}
.progress::after {
content: '';
display: inline-block;
height: v-bind(panelHeight);;
}
.progress .panel {
max-width: 100%;
white-space: nowrap;/*内容超宽后禁止换行显示*/
overflow: hidden;/*超出部分隐藏*/
text-overflow:ellipsis;/*文字超出部分以省略号显示*/
position: absolute;
left: v-bind(left);
bottom: 0;
z-index: 1;
background: #EAEDF4;
border-radius: 4px;
padding: 0 6px;
height: 22px;
line-height: 22px;
white-space: nowrap;
transform: translateX(v-bind(translateX));
transition: all .5s;
}