【基于 Vue3 的原子化时间线组件实现与应用】
基于 Vue3 的原子化时间线组件实现与应用
在前端开发中,我们经常需要使用时间线组件来展示一系列按时间顺序排列的事件。许多项目常常重复开发类似功能,导致代码冗余且维护成本高。为解决这一问题,我们设计了一个高度可定制的原子化时间线组件 base-timeline。本文将详细介绍其设计思路、实现方式、使用方法以及实际应用案例。
设计思路
时间线组件的核心是展示按时间顺序排列的事件节点,同时用线条连接各节点表示时间的延续性。在设计此组件时,我采用了以下设计理念:
- 原子化: 组件应该足够基础,不包含特定业务逻辑,只提供必要的结构和样式
- 可定制性: 支持自定义节点和线条的颜色、样式等
- 灵活的内容插槽: 通过插槽机制允许使用者完全控制节点内容的渲染方式
- 响应式设计: 适应不同屏幕尺寸和设备
- 类型安全: 使用 TypeScript 提供完整的类型定义
组件实现
基础结构
<template><div class="base-timeline"><div v-for="(item, index) in items" :key="item.id || index" class="base-timeline-item"><!-- 圆点 --><div class="base-timeline-item__node" :class="nodeClass" :style="nodeStyle"><slot name="dot" :item="item" :index="index"><div class="base-timeline-item__dot"></div></slot></div><!-- 垂直连接线 --><divv-if="!item.hideLineTail && index < items.length - 1"class="base-timeline-item__tail":style="lineStyle"></div><!-- 内容区域 --><div class="base-timeline-item__wrapper"><div class="base-timeline-item__content"><slot name="item" :item="item" :index="index"></slot></div></div></div></div><el-empty v-if="!items.length" :description="emptyText" />
</template>
属性与样式计算
const props = withDefaults(defineProps<{items: TimelineItem[];color?: string;lineColor?: string;nodeClass?: string;emptyText?: string;}>(),{color: '#0bbd87',lineColor: '',nodeClass: 'base-timeline-item__node--primary',emptyText: '暂无数据'}
);// 计算节点样式
const nodeStyle = computed(() => {return props.color ? { backgroundColor: props.color } : {};
});// 计算线条样式
const lineStyle = computed(() => {// 如果设置了专门的线条颜色,则使用线条颜色,否则使用通用颜色const lineColor = props.lineColor || props.color;return lineColor ? { borderLeftColor: lineColor } : {};
});
样式定义
.base-timeline {margin: 0;padding: 16px 0;font-size: 14px;list-style: none;// 最后一个时间线项没有尾部连接线& > div:last-child {.base-timeline-item__tail {display: none;}}
}.base-timeline-item {position: relative;padding-bottom: 24px;&__node {position: absolute;background-color: #fff;border-radius: 50%;display: flex;justify-content: center;align-items: center;left: -1px;width: 12px;height: 12px;transform: translateX(-50%);&--primary {background-color: v-bind('props.color');z-index: 2;}}&__tail {position: absolute;left: -1px;height: 100%;border-left: 2px solid v-bind('props.lineColor || props.color');transform: translateX(-50%);z-index: 1;}&__wrapper {position: relative;padding-left: 28px;top: -3px;}&__content {color: #303133;}
}
解决的痛点
- 代码复用: 避免在多个项目或多个页面中重复实现时间线组件
- 样式一致性: 确保整个应用中的时间线组件风格保持一致
- 维护成本: 集中维护核心组件,减少后期修改成本
- 可定制性: 通过属性和插槽提供高度灵活的定制能力,满足不同场景需求
- 分离关注点: 将基础UI与业务逻辑分离,使代码结构更清晰
组件的优劣势
优势
- 高度可定制: 支持自定义节点和线条的颜色、样式等
- 使用简单: API 简洁明了,容易上手
- 类型安全: 使用 TypeScript 提供完整的类型定义,减少错误
- 轻量级: 不依赖其他复杂组件库,体积小
- 节点与线条分离控制: 可以单独控制节点和连接线的颜色
劣势
- 功能相对简单: 不直接支持一些复杂功能,如交互式节点、分支时间线等
- 样式限制: 基础样式固定,某些特殊视觉效果可能需要额外定制
- 兼容性考虑: 在某些特殊浏览器可能需要额外的样式适配
使用方法
基础使用
<template><BaseTimeline :items="timelineItems" color="#409EFF"><template #item="{ item }"><div class="custom-item"><h3>{{ item.title }}</h3><p>{{ item.content }}</p><span>{{ item.time }}</span></div></template></BaseTimeline>
</template><script setup>
import { BaseTimeline } from '@/components/base-timeline';const timelineItems = [{id: 1,title: '项目启动',content: '召开项目启动会议,明确项目目标和计划',time: '2023-01-01'},{id: 2,title: '需求分析',content: '完成需求收集和分析,输出需求文档',time: '2023-01-15'},{id: 3,title: '设计阶段',content: '完成系统设计和原型设计',time: '2023-02-01'}
];
</script>
自定义节点样式
<template><BaseTimeline :items="timelineItems" color="#67C23A" lineColor="#E6E6E6"><template #dot="{ item }"><el-icon v-if="item.icon" :class="item.status"><component :is="item.icon"></component></el-icon></template><template #item="{ item }"><!-- 自定义内容 --></template></BaseTimeline>
</template>
实际应用案例
操作日志展示
以下是在实际项目中的应用案例,用于展示系统操作日志:
<template><div class="operation-log"><div class="base-title">操作记录</div><div class="log-wrapper"><!-- 使用原子化时间线组件,指定节点颜色和线条颜色 --><BaseTimeline :items="operationLogs" color="#0bbd87" lineColor="#E0E0E0"><template #item="{ item }"><div class="log-header"><div class="log-type">{{ item.operationType }}</div><div class="log-date">{{ item.operationDate }}</div></div><div class="log-detail"><div class="log-row"><div class="row-left">操作人</div><div class="row-right">{{ item.operator || '系统' }}</div></div><div class="log-row"><div class="row-left">操作记录</div><div class="row-right" :title="item.operationContent">{{ item.operationContent }}</div></div></div></template></BaseTimeline></div></div>
</template><script setup lang="ts">
import { computed } from 'vue';
import { BaseTimeline } from '@/components/base-timeline';interface OperationLogItem {operationType: string;operationDate: string;operator: string;operationContent: string;
}const props = defineProps<{logs: OperationLogItem[];
}>();// 使用计算属性确保即使传入undefined也能正确处理
const operationLogs = computed(() => props.logs || []);
</script>
项目历程展示
<template><div class="project-history"><h2>项目里程碑</h2><BaseTimeline :items="milestones" color="#409EFF" lineColor="#DCDFE6"><template #dot="{ item }"><div class="milestone-dot" :class="item.status"><i :class="getIconByStatus(item.status)"></i></div></template><template #item="{ item }"><div class="milestone-card"><div class="milestone-title"><span>{{ item.title }}</span><el-tag size="small" :type="getTagType(item.status)">{{ getStatusText(item.status) }}</el-tag></div><div class="milestone-time">{{ formatDate(item.plannedDate) }}</div><div class="milestone-desc">{{ item.description }}</div><div v-if="item.completedDate" class="milestone-completed">完成时间: {{ formatDate(item.completedDate) }}</div><div class="milestone-assignee">负责人: {{ item.assignee }}</div></div></template></BaseTimeline></div>
</template><script setup>
import { BaseTimeline } from '@/components/base-timeline';
import { computed } from 'vue';
import dayjs from 'dayjs';const props = defineProps({projectId: {type: String,required: true}
});// 假设这是从API获取的数据
const milestones = ref([{id: 'm1',title: '项目立项',plannedDate: '2023-05-10',completedDate: '2023-05-12',description: '完成项目立项报告并获得管理层批准',status: 'completed',assignee: '张经理'},{id: 'm2',title: '需求分析',plannedDate: '2023-05-20',completedDate: '2023-05-25',description: '完成用户需求访谈和需求文档编写',status: 'completed',assignee: '李分析师'},{id: 'm3',title: '系统设计',plannedDate: '2023-06-05',completedDate: '2023-06-10',description: '完成系统架构设计和数据库设计',status: 'completed',assignee: '王架构师'},{id: 'm4',title: '开发阶段',plannedDate: '2023-06-15',completedDate: null,description: '前后端开发实现',status: 'in-progress',assignee: '开发团队'},{id: 'm5',title: '系统测试',plannedDate: '2023-07-20',completedDate: null,description: '进行功能测试和性能测试',status: 'pending',assignee: '测试团队'}
]);// 格式化日期
const formatDate = (date) => {if (!date) return '未完成';return dayjs(date).format('YYYY-MM-DD');
};// 根据状态获取图标
const getIconByStatus = (status) => {const statusMap = {'completed': 'el-icon-check','in-progress': 'el-icon-loading','pending': 'el-icon-time','delayed': 'el-icon-warning'};return statusMap[status] || 'el-icon-info';
};// 根据状态获取Tag类型
const getTagType = (status) => {const typeMap = {'completed': 'success','in-progress': 'primary','pending': 'info','delayed': 'warning'};return typeMap[status] || 'info';
};// 根据状态获取文本
const getStatusText = (status) => {const textMap = {'completed': '已完成','in-progress': '进行中','pending': '待开始','delayed': '已延期'};return textMap[status] || '未知状态';
};
</script>
总结与展望
base-timeline 组件是一个轻量级、高度可定制的时间线基础组件。通过合理的设计,它实现了视图层与逻辑层的分离,使得组件能够适应各种不同的业务场景。
在实际项目中,我们已经成功将其应用于操作日志、项目历程、审批流程等多个场景,大大提高了开发效率和代码复用率。
未来,我们可以考虑以下方向进一步增强组件功能:
- 支持横向时间线
- 增加更多交互功能,如点击节点触发事件
- 支持时间线分支展示
- 提供更多预设样式主题
- 增加动画效果
通过持续迭代优化,我们相信这个组件能在更多项目中发挥重要作用,为开发者提供更好的开发体验。
下面将结合 Mermaid 图表来说明其设计思路和实现方式。
组件结构设计
组件层次结构
组件接口设计
渲染流程
以下流程图展示了组件的渲染过程:
组件属性与事件流
flowchart LRProps[输入属性] --> Component[BaseTimeline组件]Component --> DOM[渲染结果]subgraph Propsitems[items: TimelineItem[]]color[color: string]lineColor[lineColor: string]nodeClass[nodeClass: string]emptyText[emptyText: string]endsubgraph SlotsdotSlot[dot插槽]itemSlot[item插槽]endSlots --> Component
CSS样式结构
用例分析
基本用法场景
应用场景分类
mindmaproot((BaseTimeline))应用场景操作日志项目进度流程审批事件记录历史回顾自定义能力节点样式颜色图标大小线条样式颜色粗细样式内容展示卡片形式列表形式富文本
数据流转过程
下面是从组件接收数据到最终渲染的流程:
组件在业务中的应用架构
节点与线条颜色分离控制机制
下面的状态图展示了组件如何处理节点和线条颜色:
使用案例示意
以操作日志为例,展示数据如何流转:
通过这些 Mermaid 图表,我们可以更加直观地理解 base-timeline 组件的设计思路、数据流转过程以及在业务中的应用方式。这种可视化的方式有助于开发者更好地理解和使用这个组件。
这些图表展示了组件的各个方面,从内部结构到外部接口,再到实际应用场景,全面地阐述了组件的设计与实现。通过这种方式,无论是组件的开发者还是使用者,都能够快速理解组件的工作原理和使用方法。
相关文章:
【基于 Vue3 的原子化时间线组件实现与应用】
基于 Vue3 的原子化时间线组件实现与应用 在前端开发中,我们经常需要使用时间线组件来展示一系列按时间顺序排列的事件。许多项目常常重复开发类似功能,导致代码冗余且维护成本高。为解决这一问题,我们设计了一个高度可定制的原子化时间线组…...
小推桌面tv-小推电视桌面好用吗
想知道小推电视桌面是否好用?来一探究竟!小推电视桌面安全稳定,且支持自由定制。它有影视、壁纸、酒店等多种主题,适配不同场景。内置小推语音助手,还支持第三方语音助手,操作便捷。自带正版影视搜索功能&a…...
深入解析嵌入式Linux系统架构:从Bootloader到用户空间 - 结合B站视频教学
B站视频链接,请多多关注本人B站: 📌 Yocto项目实战教程:第二章 视频讲解 目录 第2章 Linux系统架构 2.1 GNU/Linux2.2 Bootloader2.3 内核空间2.4 用户空间 总结 第2章 Linux系统架构 {#linux系统架构} 嵌入式Linux系统是Linux内核的精简版…...
Asp.NET Core WebApi 配置文件
在 ASP.NET Core Web API 中,配置文件(如 appsettings.json)是管理应用程序设置的核心部分。ASP.NET Core 提供了一套灵活的配置系统,允许开发者从多种来源加载配置数据,并根据需要使用这些配置。 以下是关于如何在 A…...
pipe匿名管道实操(Linux)
管道相关函数 1 pipe 是 Unix/Linux 系统中的一个系统调用,用于创建一个匿名管道 #include <unistd.h> int pipe(int pipefd[2]); 参数说明: pipefd[2]:一个包含两个整数的数组,用于存储管道的文件描述符: pi…...
2025.04.10-拼多多春招笔试第三题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 数字重排最大化问题 问题描述 LYA是一位专业的数字设计师。她手中有两个数字序列 s 1 s_1...
Vue.js组件安全开发实战:从架构设计到攻防对抗
目录 开篇总述:安全视角下的Vue组件开发新范式 一、Vue.js组件开发现状全景扫描 二、安全驱动的Vue组件创新架构 三、工程化组件体系构建指南 四、深度攻防对抗实战解析 五、安全性能平衡策略 结语:安全基因注入前端开发的未来展望 下期预告&…...
质因数之和-蓝桥20249
题目: 代码:无脑直接根据题目,一步步操作就行 #include <iostream> using namespace std;int gcd(int a,int b){if(b0) return a;return gcd(b,a%b); }bool exist_gcd(int a,int b){if(gcd(a,b)1) return false;return true; }bool is…...
《栈区、堆区和静态区:内存管理的三大支柱》
🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言 🌍文章目入 一、栈区(Stack)(一)栈区的定义(二)栈区的特点(三)栈区的使用…...
Rust Command无法执行*拓展解决办法
async fn run_cmd_async_out<I, S>(cmd: &str, args: I, timeout_s: u64, with_http_proxy: bool) -> Result<String> whereI: IntoIterator<Item S>,S: AsRef<OsStr>, {let mut cmd tokio::process::Command::new(cmd);// 让 sh 来运行命令&…...
AI 笔记 - 开源轻量级人脸检测项目
开源轻量级人脸检测项目 引言项目解析[libfacedetection 于仕琪](https://github.com/ShiqiYu/libfacedetection)[Ultra-Light-Fast-Generic-Face-Detector-1MB Linzaer](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB)[A-Light-and-Fast-Face-Detec…...
AI Agent vs 大模型
一句话概述 大模型是“超级学霸”,负责理解、思考和生成内容; AI Agent是“行动派秘书”,会调用工具和知识库,自主完成任务。 角色定位对比 大模型 智能体 核心 任务 回答、创作、推理、分析 规划、决策、执行、协调多工具 …...
go游戏后端开发32:自摸杠处理逻辑
当我们在自摸杠时,实际上在杠完之后,我们还需要进行一个删除操作。因此,我们需要在上面拷贝一个删除操作。由于这是自摸杠,所以这个地方需要删除四次。在这里,我们需要注意的是,自摸杠时,传过来…...
今日行情明日机会——20250411
今天缩量,上方压力依然在,外围还在升级,企稳还需要时日。 2025年4月11日A股涨停主要行业方向分析 一、核心主线方向 芯片(半导体) • 涨停家数:24家(当日最强方向)。 • 驱动逻辑&…...
【Linux】TCP_Wrappers+iptables实现堡垒机功能
规划 显示jumpserver的简单功能,大致的网络拓扑图如下 功能规划 & 拓扑结构 JumpServer(堡垒机)主要功能: 对访问目标服务器进行统一入口控制(例如 nginx、mysql、redis)。使用 iptables 做 NAT 转…...
git仓库中.git文件夹过大的问题
由于git仓库中存放了较大的文件,之后即使在gitignore中添加,也不会导致.git文件夹变小。 参考1 2 通过 du -d 1 -h查看文件大小 使用 git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail…...
ssh 登录报错集合(FQA)
1、使用root登录失败(远程主机不允许root登录) 问题:通过 ssh 连接远程主机( ubuntu )时报错,Permission denied 如下: 解决方法:确认root的登录密码没错,且可以正常与远…...
NO.89十六届蓝桥杯备战|动态规划-分组背包-混合背包-多维费用背包|通天之分组背包|排兵布阵|樱花|L国的战斗间谍(C++)
P1757 通天之分组背包 - 洛谷 因为⼀个组⾥⾯最多只能挑⼀个元素,所以我们就以⼀个组为单位。 状态表⽰: dp[i][j]表⽰从前i 组中挑选物品,总重量不超过j 的情况下,最⼤的价值。 那么dp[n][m]就是最终结果。状态转移⽅程&#x…...
NVIDIA H100 vs A100:新一代GPU架构性能对比分析
一、核心架构演进对比 Ampere架构(A100)采用台积电7nm工艺,集成540亿晶体管,配备6,912个CUDA核心和432个第三代Tensor Core,支持FP16、TF32和INT8精度计算。其显存子系统采用HBM2e技术,80GB版本带宽可…...
使用Mybatis时在XML中SQL高亮显示的方法
如图所示,上方的SQL代码很像是一个字符串,那么如何把上方的SQL改成和下方一样的SQL,使得IDEA可以识别SQL方言呢? 1.选中SQL中的一部分代码,此时左侧会出现一个黄色的灯泡图案,点击2.选择这个注入语言或者引用...
机场跑道异物检测数据集VOC+YOLO格式33793张31类别
数据集分辨率都是300x300,都是贴近地面拍摄,具体看图片 据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):33793 标注数量(xml文件…...
掌握C语言文件操作:从理论到实战指南
文件操作是C语言编程中不可或缺的一部分,它使得程序能够持久化存储数据,并在需要时高效读写。本文将从基础概念到实战技巧,系统讲解C语言文件操作的核心知识点,并结合代码示例帮助读者深入理解。 一. 为什么需要文件操作…...
如何进行预算考核
✅ 一、预算考核体系总体架构 模块内容说明考核内容1. 预算目标/指标完成情况2. 预算编制/执行情况双轮驱动,目标 + 执行双考核考核对象高层、中层、基层、后台支持分层分类考核考核周期月度(滚动)+ 季度(校验)+ 年度(决算)提高适应性和准确性考核工具指标体系、差错率评…...
在 Linux 上安装 MongoDB Shell
1. 下载 MongoDB Shell Download | MongoDB wget https://downloads.mongodb.com/compass/mongosh-2.5.0-linux-x64.tgz 2. tar -zxvf mongosh-2.5.0-linux-x64.tgz 3. copy 命令 sudo cp mongosh /usr/local/bin/ sudo cp mongosh_crypt_v1.so /usr/local/lib/ 4. …...
数据结构-复杂度详解
前言:大家好!本文带来的是数据结构-复杂度的讲解,一起来看看吧! 1.算法的时间复杂度和空间复杂度 1.1算法的效率 复杂度:衡量一个算法的好坏(效率),从两个维度衡量,时…...
安宝特新闻丨Vuzix Core™波导助力AR,视角可调、高效传输,优化开发流程
Vuzix Core™ 光波导技术 近期,Vuzix Core™光波导技术赋能AR新视界!该系列镜片支持定制化宽高比调节及20至40视场角范围,可灵活适配各类显示引擎。通过创新的衍射光波导架构,Vuzix Core™实现了光学传输效率与图像质量的双重突破…...
【SQL】常见SQL 行列转换的方法汇总 - 精华版
【SQL】常见SQL 行列转换的方法汇总 - 精华版 一、引言二、SQL常见的行列转换对比1. 行转列 Pivoting1.1 CASE WHEN 聚合函数1.2 IF 聚合函数1.3 PIVOT操作符 2.列转行 Unpivoting2.1 UNION ALL2.2 EXPLODE函数(Hive/Spark&#…...
【原创】vue-element-admin-plus完成确认密码功能,并实时获取Form中表单字段中的值
前言 我第一句就想说:vue-element-admin-plus真是个大坑货!就一个确认密码功能都值得我单开一页博客来讲这么一个简单的功能 布局和代码 布局如图所示,我需要密码和确认密码,确认密码需要和密码中的内容一致,不然会返…...
Vue3中watch监视reactive对象方法详解
在Vue3中,使用watch监视reactive对象时,需根据监视的目标选择合适的方法。以下是详细的步骤和说明: 1. 监视整个reactive对象 自动深度监视:直接监视reactive对象时,Vue3会默认启用深度监视,无需设置deep:…...
PyTorch实现多输入输出通道的卷积操作
本文通过代码示例详细讲解如何在PyTorch中实现多输入通道和多输出通道的卷积运算,并对比传统卷积与1x1卷积的实现差异。 1. 多输入通道互相关运算 当输入包含多个通道时,卷积核需要对每个通道分别进行互相关运算,最后将结果相加。以下是实现…...
