CircularProgress
This commit is contained in:
parent
9d6086726d
commit
79bd72febb
|
@ -0,0 +1,157 @@
|
||||||
|
<template>
|
||||||
|
<div class="circular-progress-container">
|
||||||
|
<div class="circular-progress" :style="{ width: size + 'px', height: size + 'px' }">
|
||||||
|
<svg class="circular-progress-svg" :width="size" :height="size">
|
||||||
|
<!-- 背景圆环 -->
|
||||||
|
<circle
|
||||||
|
class="circular-progress-bg"
|
||||||
|
:cx="center"
|
||||||
|
:cy="center"
|
||||||
|
:r="radius"
|
||||||
|
:stroke-width="strokeWidth"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<!-- 进度圆环 -->
|
||||||
|
<circle
|
||||||
|
class="circular-progress-fill"
|
||||||
|
:cx="center"
|
||||||
|
:cy="center"
|
||||||
|
:r="radius"
|
||||||
|
:stroke-width="strokeWidth"
|
||||||
|
fill="none"
|
||||||
|
:stroke-dasharray="circumference"
|
||||||
|
:stroke-dashoffset="strokeDashoffset"
|
||||||
|
:style="{ stroke: progressColor }"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<!-- 中心文本 -->
|
||||||
|
<div class="circular-progress-text" v-if="showText">
|
||||||
|
<span class="progress-percent">{{ percent }}%</span>
|
||||||
|
<span class="progress-label" v-if="label">{{ label }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
percent: number
|
||||||
|
size?: number
|
||||||
|
strokeWidth?: number
|
||||||
|
showText?: boolean
|
||||||
|
label?: string
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
size: 60,
|
||||||
|
strokeWidth: 4,
|
||||||
|
showText: true,
|
||||||
|
label: '',
|
||||||
|
color: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算圆环参数
|
||||||
|
const center = computed(() => props.size / 2)
|
||||||
|
const radius = computed(() => (props.size - props.strokeWidth) / 2)
|
||||||
|
const circumference = computed(() => 2 * Math.PI * radius.value)
|
||||||
|
const strokeDashoffset = computed(() => circumference.value - (props.percent / 100) * circumference.value)
|
||||||
|
|
||||||
|
// 进度颜色
|
||||||
|
const progressColor = computed(() => {
|
||||||
|
if (props.color) return props.color
|
||||||
|
|
||||||
|
if (props.percent >= 80) return '#52c41a'
|
||||||
|
if (props.percent >= 60) return '#1890ff'
|
||||||
|
if (props.percent >= 40) return '#faad14'
|
||||||
|
if (props.percent > 0) return '#ff4d4f'
|
||||||
|
return '#d9d9d9'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.circular-progress-container {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress-svg {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress-bg {
|
||||||
|
stroke: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress-fill {
|
||||||
|
transition: stroke-dashoffset 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress-text {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-percent {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1d2129;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-label {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #86909c;
|
||||||
|
margin-top: 2px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不同尺寸的样式调整
|
||||||
|
.circular-progress[style*="width: 40px"],
|
||||||
|
.circular-progress[style*="width: 40px;"] {
|
||||||
|
.progress-percent {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-label {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress[style*="width: 80px"],
|
||||||
|
.circular-progress[style*="width: 80px;"] {
|
||||||
|
.progress-percent {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular-progress[style*="width: 100px"],
|
||||||
|
.circular-progress[style*="width: 100px;"] {
|
||||||
|
.progress-percent {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-label {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue