feat(support-activity): add VerticalProgressBar component
This commit is contained in:
parent
cb648d2cb0
commit
a8777cb1ad
@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<view class="v-progress">
|
||||||
|
<!-- 目标数字(位于目标圆上方) -->
|
||||||
|
<text v-if="showText" class="v-target-text">{{ formattedTarget }}</text>
|
||||||
|
|
||||||
|
<!-- 轨道 + 进度层 -->
|
||||||
|
<view
|
||||||
|
class="v-track"
|
||||||
|
:style="{ width: barWidth, height: barHeight }"
|
||||||
|
>
|
||||||
|
<!-- 已填充(从底部起) -->
|
||||||
|
<view class="v-fill" :style="{ height: fillHeight }" />
|
||||||
|
|
||||||
|
<!-- 目标圆(固定在顶部) -->
|
||||||
|
<view
|
||||||
|
class="v-circle v-target-circle"
|
||||||
|
:style="{ width: circleSize, height: circleSize }"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 当前圆(随 progress 上移) -->
|
||||||
|
<view
|
||||||
|
class="v-circle v-current-circle"
|
||||||
|
:style="{ top: circleTop, width: circleSize, height: circleSize }"
|
||||||
|
>
|
||||||
|
<text v-if="showText" class="v-current-text">{{ formattedCurrent }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
current: { type: Number, default: 0 },
|
||||||
|
target: { type: Number, default: 100 },
|
||||||
|
barHeight: { type: String, default: '200rpx' },
|
||||||
|
barWidth: { type: String, default: '16rpx' },
|
||||||
|
circleSize: { type: String, default: '60rpx' },
|
||||||
|
fillColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'linear-gradient(180deg, #FFD700, #FFA500)'
|
||||||
|
},
|
||||||
|
trackColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'rgba(255, 255, 255, 0.2)'
|
||||||
|
},
|
||||||
|
textColor: { type: String, default: '#FFD700' },
|
||||||
|
targetColor: { type: String, default: 'rgba(255, 255, 255, 0.8)' },
|
||||||
|
showText: { type: Boolean, default: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 进度比例(0~1),将来要换公式只改这一处
|
||||||
|
const ratio = computed(() => {
|
||||||
|
if (props.target === 0) return 0
|
||||||
|
return Math.min(props.current / props.target, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 已填充高度:ratio × 100%(从底部起)
|
||||||
|
const fillHeight = computed(() => ratio.value * 100 + '%')
|
||||||
|
|
||||||
|
// 当前圆位置:(1 - ratio) × 100%(从顶部起)
|
||||||
|
const circleTop = computed(() => (1 - ratio.value) * 100 + '%')
|
||||||
|
|
||||||
|
// 数字本地化:用 ref + watch 避免每帧调用 toLocaleString
|
||||||
|
const formattedCurrent = ref(props.current.toLocaleString())
|
||||||
|
const formattedTarget = ref(props.target.toLocaleString())
|
||||||
|
|
||||||
|
watch(() => props.current, (v) => {
|
||||||
|
formattedCurrent.value = v.toLocaleString()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.target, (v) => {
|
||||||
|
formattedTarget.value = v.toLocaleString()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.v-progress {
|
||||||
|
position: fixed;
|
||||||
|
left: 24rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 50;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
/* 其它样式(背景框、阴影等)留空,方便用户自行调 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-target-text {
|
||||||
|
color: v-bind(targetColor);
|
||||||
|
font-size: 20rpx;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-track {
|
||||||
|
position: relative;
|
||||||
|
background: v-bind(trackColor);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
/* 关键:让圆能露出轨道边界 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-fill {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: v-bind(fillColor);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-circle {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-target-circle {
|
||||||
|
top: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
/* 目标圆不显示数字 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-current-circle {
|
||||||
|
background: v-bind(fillColor);
|
||||||
|
color: v-bind(textColor);
|
||||||
|
transition: top 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-current-text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: v-bind(textColor);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user