news 2026/8/29 13:09:39

Vue3时间轴(Timeline)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue3时间轴(Timeline)

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>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 13:08:54

IIS3DHHC高精度加速度计实战:从微倾角测量到校准

以前做高精度倾角测量&#xff0c;我手上一直备着好几颗加速度计&#xff0c;但到了要测0.01级别的倾斜变化时&#xff0c;普通消费级传感器的底噪直接就让人没法继续。后来换到IIS3DHHC这颗3轴数字加速度计&#xff0c;噪声密度能到15g/√Hz级别&#xff0c;温度稳定性也明显比…

作者头像 李华
网站建设 2026/8/29 13:07:01

CodeWhisperer 免费版够用?3 个月数据漂移监控打脸记

CodeWhisperer 免费版够用?3 个月数据漂移监控打脸记 发版当天早上,线上推荐服务的点击率告警让我第一次直面“数据漂移”这个词。老同事说可能是特征分布偏移了,我当时还觉得重新训练一遍就能解决。三天后,我对着统计脚本里那条忽高忽低的 PSI 曲线,才意识到数据漂移是个持续…

作者头像 李华
网站建设 2026/8/29 13:06:01

2024年新疆河流水系SHP数据下载、处理与应用全攻略

简介&#xff1a;在GIS工程实践中&#xff0c;矢量数据是空间分析与制图表达的核心载体。无论是水文分析、流域规划&#xff0c;还是工程选线与应急管理&#xff0c;一套结构清晰、坐标系准确的水系数据往往决定项目效率。以Shapefile这一通用格式为例&#xff0c;理解其文件构…

作者头像 李华
网站建设 2026/8/29 13:03:39

JWT的Payload部分通常包含哪些内容?

JWT的Payload部分&#xff0c;我们可以把它想象成“通行证”里面的主要内容区域。这部分描述了通行证持有者的信息和他们所拥有的权限。Payload通常包含的内容&#xff1a;注册声明&#xff08;Registered Claims&#xff09;&#xff1a;这是一些预定义好的声明&#xff0c;比…

作者头像 李华
网站建设 2026/8/29 13:01:06

Replit Free Mode与Routines:零门槛云端开发与自动化任务实践

Replit 这次把 Free Mode 和 Routines 放在一起更新&#xff0c;我第一反应是&#xff1a;它终于把“能跑”和“每次都不用重新跑”两件事打通了。Free Mode 解决的是入门门槛&#xff0c;让不装本地环境的人也能快速启动项目&#xff1b;Routines 解决的是重复劳动&#xff0c…

作者头像 李华
网站建设 2026/8/29 13:00:53

基于BERT的中文情感分类实战:从环境搭建到模型部署全流程解析

简介&#xff1a;自然语言处理&#xff08;NLP&#xff09;是人工智能的核心领域之一&#xff0c;旨在让计算机理解、解释和生成人类语言。其基本原理是通过算法模型从文本数据中学习语言规律。在众多NLP技术中&#xff0c;预训练模型通过在大规模语料上预先学习通用语言表示&a…

作者头像 李华