Vue2时间轴(Timeline)
可自定义设置以下属性:
时间轴内容数组(items),类型:Item[],默认 []
时间轴区域总宽度(width),类型:number|string,单位 px,默认 '100%'
时间线样式(lineStyle),类型: 'solid' | 'dashed' | 'dotted',默认 'solid'
通过设置 mode 可以改变时间轴和内容的相对位置(mode),类型:'left' | 'center' | 'right',默认 'left
当 mode 为 center 时,内容交替展现,内容从左边(left)开始或者右边(right)开始展现(position),类型:'left' | 'right',默认:'left'
时间轴内容(Item):
desc:文字描述,类型:string | slot,默认 undefined
color?:圆圈颜色,类型:'blue' | 'green' | 'red' | 'gray' | string,默认 'blue'
效果如下图:
在线预览
①创建时间轴组件Timeline.vue:
<script setup lang="ts"> import { ref, computed, watchEffect } from 'vue' export interface Item { desc: string // 文字描述 string | slot color?: 'blue' | 'green' | 'red' | 'gray' | string // 圆圈颜色,默认值 blue } export interface Props { items?: Item[] // 时间轴内容数组 width?: number | string // 时间轴区域总宽度,单位 px lineStyle?: 'solid' | 'dashed' | 'dotted' // 时间线样式 mode?: 'left' | 'center' | 'right' // 通过设置 mode 可以改变时间轴和内容的相对位置 position?: 'left' | 'right' // 当 mode 为 center 时,内容交替展现,内容从左边(left)开始或者右边(right)开始展现 } const props = withDefaults(defineProps<Props>(), { items: () => [], width: '100%', lineStyle: 'solid', mode: 'left', position: 'left' }) // 颜色主题对象 enum ColorStyle { blue = '#1677ff', green = '#52c41a', red = '#ff4d4f', gray = '#00000040' } const descRef = ref() const dotsHeight = ref<string[]>([]) const totalWidth = computed(() => { if (typeof props.width === 'number') { return `${props.width}px` } else { return props.width } }) const len = computed(() => { return props.items.length }) function getDotsHeight() { for (let n = 0; n < len.value; n++) { dotsHeight.value[n] = getComputedStyle( descRef.value[n].firstElementChild || descRef.value[n], null ).getPropertyValue('line-height') } } watchEffect( () => { getDotsHeight() }, { flush: 'post' } ) watchEffect( () => { if (props.mode === 'center') { for (let n = 0; n < len.value; n++) { if ((n + 1) % 2) { // odd if (props.position === 'left') { descRef.value[n].classList.add('desc-alternate-left') } else { descRef.value[n].classList.add('desc-alternate-right') } } else { // even if (props.position === 'left') { descRef.value[n].classList.add('desc-alternate-right') } else { descRef.value[n].classList.add('desc-alternate-left') } } } } }, { flush: 'post' } ) </script> <template> <div class="timeline-wrap" :style="`width: ${totalWidth};`"> <div class="timeline-item" :class="{ 'item-last': index === items.length - 1 }" v-for="(item, index) in items" :key="index" > <span class="timeline-tail" :class="`tail-${mode}`" :style="`border-left-style: ${lineStyle};`"></span> <div class="timeline-dot" :class="`dot-${mode}`" :style="`height: ${dotsHeight[index]}`"> <slot name="dot" :item="item" :index="index"> <span class="dot-item" v-if="item.color === 'red'" :style="{ borderColor: ColorStyle.red }"></span> <span class="dot-item" v-else-if="item.color === 'gray'" :style="{ borderColor: ColorStyle.gray }"></span> <span class="dot-item" v-else-if="item.color === 'green'" :style="{ borderColor: ColorStyle.green }"></span> <span class="dot-item" v-else-if="item.color === 'blue'" :style="{ borderColor: ColorStyle.blue }"></span> <span class="dot-item" v-else :style="{ borderColor: item.color || ColorStyle.blue }"></span> </slot> </div> <div ref="descRef" :class="`timeline-desc desc-${mode}`"> <slot name="desc" :item="item" :index="index">{{ item.desc || '--' }}</slot> </div> </div> </div> </template> <style lang="less" scoped> .timeline-wrap { font-size: 14px; color: rgba(0, 0, 0, 0.88); line-height: 1.5714285714285714; .timeline-item { position: relative; padding-bottom: 30px; .timeline-tail { position: absolute; top: 12px; width: 0; height: 100%; border-left-width: 2px; border-left-color: #e8e8e8; } .tail-left { left: 5px; } .tail-center { left: 0; right: 0; margin: 0 auto; } .tail-right { right: 5px; } .timeline-dot { position: absolute; display: flex; align-items: center; :deep(svg) { fill: currentColor; } .dot-item { display: inline-block; width: 12px; height: 12px; border-width: 2px; border-style: solid; border-radius: 50%; background: #fff; } } .dot-left { left: 6px; transform: translateX(-50%); } .dot-center { left: 50%; transform: translateX(-50%); } .dot-right { right: 6px; transform: translateX(50%); } .timeline-desc { font-size: 14px; line-height: 1.5714285714285714; word-break: break-all; } .desc-left { margin-left: 25px; } .desc-center { width: calc(50% - 12px); } .desc-alternate-left { text-align: end; } .desc-alternate-right { margin-left: calc(50% + 12px); } .desc-right { margin-right: 25px; text-align: end; } } .item-last { .timeline-tail { display: none; } } } </style>②在要使用的页面引入:
<script setup lang="ts"> import Timeline from './Timeline.vue' import { ref } from 'vue' import { ClockCircleOutlined } from '@ant-design/icons-vue' import type { TimelineItem } from 'vue-amazing-ui' const timelineItems = ref<TimelineItem[]>([ { desc: 'Create a services site 2023-05-24', color: 'green' }, { desc: 'Solve initial network problems 1 Solve initial network problems 2 2023-05-24', color: 'red' }, { desc: 'Technical testing 2023-05-24', color: 'blue' }, { desc: 'Network problems being solved 2023-05-24' }, { desc: 'Network problems being solved 2', color: 'gray' } ]) </script> <template> <div> <h1>{{ $route.name }} {{ $route.meta.title }}</h1> <h2 class="mt30 mb10">基本使用</h2> <Timeline :items="timelineItems" /> <h2 class="mb10">自定义样式</h2> <Timeline :items="timelineItems"> <template #dot="{ index }"> <span class="big-dot" v-if="index === 2"></span> <ClockCircleOutlined v-if="index === 3" style="font-size: 16px; color: #1668dc; background: #fff; border-radius: 50%" /> </template> <template #desc="{ index }"> <p class="desc" v-if="index === 2">Create a services site</p> </template> </Timeline> <h2 class="mb10">使用虚线</h2> <Timeline :items="timelineItems" line-style="dashed" /> <h2 class="mb10">右侧时间轴点</h2> <Timeline :items="timelineItems" mode="right" :width="540" /> <h2 class="mb10">中间时间轴点</h2> <h3 class="mb10">内容从左边开始交替展现</h3> <Timeline :items="timelineItems" mode="center" :width="540"> <template #dot="{ index }"> <span class="big-dot" v-if="index === 2"></span> </template> </Timeline> <h3 class="mb10">内容从右边开始交替展现</h3> <Timeline :items="timelineItems" mode="center" position="right" :width="640"> <template #dot="{ index }"> <span class="big-dot" v-if="index === 2"></span> </template> </Timeline> <h3 class="mb10">水平时间轴</h3> <Timeline horizontal :items="timelineItems"> <template #dot="{ index }"> <span class="big-dot" v-if="index === 2"></span> </template> </Timeline> </div> </template> <style lang="less" scoped> .big-dot { display: inline-block; width: 18px; height: 18px; border: 4px solid #1677ff; border-radius: 50%; background: #fff; } .desc { font-size: 16px; font-weight: 500; } </style>