Vue2进度条(Progress)
可自定义设置以下属性:
进度条宽度(width),单位 px,类型:string | number,默认 undefined;type: 'line' 时,为进度条宽度,默认值 '100%';type: 'circle' 时,为进度圈宽高,默认值 120
当前进度百分比(percent),类型:number,默认 0
进度条的尺寸(lineSize),单位 px,类型:number,默认 undefined;type: 'line' 时,为进度条线高,默认值 8;type: 'circle' 时,单位是进度圈画布宽度的百分比,默认值 6
进度条的色彩(lineColor),类型:string | {'0%'?: string, '100%'?: string, from?: string, to?: string, direction?: 'left'|'right'},默认 undefined;传入 string 时为纯色,传入 object 时为渐变,进度圈时 direction: 'left' 为逆时针,direction: 'right' 为顺时针
进度条边缘的形状(lineCap),类型:'round' | 'square',默认 'round'
是否显示进度数值或状态图标(showInfo),类型:boolean,默认 true
进度数值或状态图标的尺寸(infoSize),单位 px,类型:number,默认 undefined;type: 'line' 时,默认值 14;type: 'circle' 时,默认值 undefined
进度完成时的信息(success),类型:string | slot,默认 undefined
内容的模板函数(format),类型:(percent: number) => (string | number) | slot,默认:(percent: number) => percent + '%'
进度条类型(type),类型:'line' | 'circle',默认 'line'
效果如下图:
在线预览
①创建进度条组件Progress.vue:
其中引入使用了以下工具函数:
- 监听插槽存在 useSlotsExist
获取依赖注入 useInject
<script setup lang="ts"> import { computed } from 'vue' import { useSlotsExist, useInject } from 'components/utils' export interface Gradient { '0%'?: string '100%'?: string from?: string to?: string direction?: 'left' | 'right' // 默认 'right' } export interface Props { width?: number | string // 进度条宽度,单位 px;type: 'line' 时,为进度条宽度,默认值 '100%';type: 'circle' 时,为进度圈宽高,默认值 120 percent?: number // 当前进度百分比 lineSize?: number // 进度条的尺寸,单位 px;type: 'line' 时,为进度条线高,默认值 8;type: 'circle' 时,单位是进度圈画布宽度的百分比,默认值 6 lineColor?: string | Gradient // 进度条的色彩,传入 string 时为纯色,传入 Gradient 时为渐变,进度圈时 direction: 'left' 为逆时针,direction: 'right' 为顺时针 lineCap?: 'round' | 'butt' // 进度条边缘的形状 showInfo?: boolean // 是否显示进度数值或状态图标 infoSize?: number // 进度数值或状态图标的尺寸,单位 px;type: 'line' 时,默认值 14;type: 'circle' 时,默认值 24 success?: string // 进度完成时的信息 string | slot format?: (percent: number) => string | number // 内容的模板函数 function | slot type?: 'line' | 'circle' // 进度条类型 } const props = withDefaults(defineProps<Props>(), { width: undefined, percent: 0, lineSize: undefined, lineColor: undefined, lineCap: 'round', showInfo: true, infoSize: undefined, success: undefined, format: (percent: number) => percent + '%', type: 'line' }) const { colorPalettes } = useInject('Progress') // 主题色注入 const slotsExist = useSlotsExist(['success']) // 进度条宽度/进度圈宽高 const progressSize = computed(() => { if (props.width === undefined) { if (props.type === 'line') { return '100%' } if (props.type === 'circle') { return '120px' } } return typeof props.width === 'number' ? `${props.width}px` : props.width }) // 进度条高度;进度圈线宽 const progressLineSize = computed(() => { if (props.lineSize === undefined) { if (props.type === 'line') { return 8 } if (props.type === 'circle') { return 6 } } return props.lineSize as number }) // 进度条/圈进度数值或状态图标尺寸 const progressInfoSize = computed(() => { if (props.infoSize === undefined) { if (props.type === 'line') { return '14px' } if (props.type === 'circle') { return '24px' } } return `${props.infoSize}px` }) // 圆条周长 const perimeter = computed(() => { return (100 - progressLineSize.value) * Math.PI }) // 圆条轨道路径指令 const path = computed(() => { const long = 100 - progressLineSize.value return `M 50,50 m 0,-${long / 2} a ${long / 2},${long / 2} 0 1 1 0,${long} a ${long / 2},${long / 2} 0 1 1 0,-${long}` }) // 是否为渐变色 const isGradientColor = computed(() => { if (props.lineColor !== undefined && typeof props.lineColor !== 'string') { return true } return false }) // 进度条/圈颜色 const lineColorComputed = computed(() => { if (props.lineColor === undefined) { return colorPalettes.value[5] } else if (typeof props.lineColor === 'string') { return props.lineColor } else { return `linear-gradient(to ${props.lineColor.direction || 'right'}, ${props.lineColor['0%'] || props.lineColor.from}, ${props.lineColor['100%'] || props.lineColor.to})` } }) // 进度圈渐变色 id const circleGradient = computed(() => { if (isGradientColor.value) { const gradientColor = props.lineColor as Gradient if (gradientColor.direction === undefined || gradientColor.direction === 'right') { return `right-${gradientColor['0%'] || gradientColor.from}-${gradientColor['100%'] || gradientColor.to}` } return `left-${gradientColor['100%'] || gradientColor.to}-${gradientColor['0%'] || gradientColor.from}` } return null }) const circleColorFrom = computed(() => { if (isGradientColor.value) { const gradientColor = props.lineColor as Gradient if (gradientColor.direction === undefined || gradientColor.direction === 'right') { return gradientColor['0%'] || gradientColor.from } else { return gradientColor['100%'] || gradientColor.to } } return }) const circleColorTo = computed(() => { if (isGradientColor.value) { const gradientColor = props.lineColor as Gradient if (!gradientColor.direction || gradientColor.direction === 'right') { return gradientColor['100%'] || gradientColor.to } else { return gradientColor['0%'] || gradientColor.from } } return }) const showPercent = computed(() => { return props.format(props.percent > 100 ? 100 : props.percent) }) const showSuccess = computed(() => { return slotsExist.success || props.success }) </script> <template> <div v-if="type === 'line'" class="progress-line-wrap" :style="` --progress-size: ${progressSize}; --progress-primary-color: ${lineColorComputed}; --progress-success-color: #52c41a; --progress-font-size: ${progressInfoSize}; --progress-border-radius: ${lineCap === 'round' ? '100px' : 0}; `" > <div class="progress-inner"> <div :class="['progress-bg', { 'line-success': percent >= 100 && !isGradientColor }]" :style="`width: ${percent >= 100 ? 100 : percent}%; height: ${progressLineSize}px;`" ></div> </div> <template v-if="showInfo"> <Transition name="fade" mode="out-in"> <span v-if="percent >= 100" class="progress-success"> <svg v-if="showSuccess === undefined" class="icon-svg" focusable="false" ><script setup lang="ts"> import Progress from './Progress.vue' import { ref } from 'vue' import { MinusOutlined, PlusOutlined } from '@ant-design/icons-vue' import type { ProgressProps } from 'vue-amazing-ui' const percent = ref(80) const lineCapOptions = [ { label: 'round', value: 'round' }, { label: 'butt', value: 'butt' } ] const lineCap = ref<ProgressProps['lineCap']>('butt') function onIncrease(scale: number) { const res = percent.value + scale if (res > 100) { percent.value = 100 } else { percent.value = res } } function onDecline(scale: number) { const res = percent.value - scale if (res < 0) { percent.value = 0 } else { percent.value = res } } </script> <template> <div style="width: 900px"> <h1>{{ $route.name }} {{ $route.meta.title }}</h1> <h2 class="mt30 mb10">基本使用</h2> <Progress :percent="percent" /> <h2 class="mt30 mb10">进度圈</h2> <Space align="center"> <Progress type="circle" :percent="percent" /> <Button @click="onDecline(5)" size="large" :icon="MinusOutlined">Decline</Button> <Button @click="onIncrease(5)" size="large" :icon="PlusOutlined">Increase</Button> </Space> <h2 class="mt30 mb10">完成进度条</h2> <Flex vertical> <Progress :percent="100" /> <Progress type="circle" :percent="100" /> </Flex> <h2 class="mt30">渐变进度条</h2> <h3 class="mb10"> strokeColor: { '0%': '#108ee9', '100%': '#87d068', direction: 'right' } 或 { from: '#108ee9', to: '#87d068', direction: 'right' } </h3> <Flex vertical> <Progress :line-color="{ '0%': '#108ee9', '100%': '#87d068' }" :percent="percent" /> <Space align="center"> <Progress type="circle" :line-color="{ '0%': '#108ee9', '100%': '#87d068' }" :percent="percent" /> <Button @click="onDecline(5)" size="large" :icon="MinusOutlined">Decline</Button> <Button @click="onIncrease(5)" size="large" :icon="PlusOutlined">Increase</Button> </Space> </Flex> <h2 class="mt30 mb10">自定义样式</h2> <Flex vertical> <Progress style="--progress-success-color: #ff6900" :line-size="24" :line-color="{ '0%': '#108ee9', '100%': '#87d068', direction: 'left' }" :info-size="24" :percent="percent" /> <Space align="center"> <Progress style="--progress-success-color: #ff6900" type="circle" :width="180" :line-size="14" :line-color="{ '0%': '#108ee9', '100%': '#87d068', direction: 'left' }" :info-size="28" :percent="percent" /> <Button @click="onDecline(5)" size="large" :icon="MinusOutlined">Decline</Button> <Button @click="onIncrease(5)" size="large" :icon="PlusOutlined">Increase</Button> </Space> </Flex> <h2 class="mt30 mb10">自定义边缘形状</h2> <Flex vertical> <Radio :options="lineCapOptions" v-model:value="lineCap" button button-style="solid" /> <Progress :line-size="20" :line-color="{ '0%': 'white', '100%': 'pink' }" :line-cap="lineCap" :info-size="20" :percent="percent" /> <Space align="center"> <Progress type="circle" :width="160" :line-size="12" :line-color="{ '0%': '#e3f2fd', '100%': '#2080f0' }" :line-cap="lineCap" :info-size="24" :percent="percent" /> <Button @click="onDecline(5)" size="large" :icon="MinusOutlined">Decline</Button> <Button @click="onIncrease(5)" size="large" :icon="PlusOutlined">Increase</Button> </Space> </Flex> <h2 class="mt30 mb10">自定义文字</h2> <Flex vertical> <Progress :line-size="20" :info-size="20" :percent="percent" :format="(percent: number) => `$${percent}`" success="Done" /> <Progress style="--success-color: #d48806" :line-size="20" :info-size="20" :percent="percent"> <template #format="{ percent }"> <span style="color: #d4380d">{{ percent }}%</span> </template> <template #success> <span style="color: #d48806">Bingo</span> </template> </Progress> <Space align="center"> <Progress type="circle" :width="160" :line-size="12" :info-size="24" :percent="percent" :format="(percent: number) => `${percent} Days`" success="Done" /> <Progress style="--success-color: #d48806" type="circle" :width="160" :line-size="12" :info-size="24" :percent="percent" > <template #format="{ percent }"> <span style="color: #d4380d">{{ percent }}%</span> </template> <template #success> <span style="color: #d48806">Bingo</span> </template> </Progress> <Button @click="onDecline(5)" size="large" :icon="MinusOutlined">Decline</Button> <Button @click="onIncrease(5)" size="large" :icon="PlusOutlined">Increase</Button> </Space> </Flex> </div> </template>