当前位置: 首页 > news >正文

vue3写一个简单的时间轴组件

插件版本:

"element-plus": "^2.3.12"

"vue": "^3.0.0"

代码示例:

样式文件style.less:

.el-popper.o-el-tooltip-popper-class {max-width: 300px;white-space: pre-wrap;}

MyTimeLineCol组件:

<template><div class="o-timeline-area" :style="`width: ${width}px`"><div class="o-timeline"><divv-for="(item, index) in timelineDesc":key="index":class="['o-timeline-item', { last: index === timelineDesc.length - 1 }]"><div class="o-tail" /><divclass="o-finish":style="{'--rate': item.ratePercent,'--borderColor': item.rate == 100 ? GREEN_COLOR : BLUE_COLOR}"/><div class="o-dot"><spanclass="solid-dot":class="item.rate == 100 ? 'green' : item.rate == 0 ? 'gray' : 'blue'"></span></div><el-tooltippopper-class="o-el-tooltip-popper-class"effect="dark":content="`${item.leftTitle || ''} ${item.planTime}~${item.finishTime} ${item.desc || '--'}${'\n'}${item.title} ${item.text}`"placement="top"><div class="o-content"><div:class="[display === 'right-only'? 'o-ang-left': index % 2 === 0? 'o-ang-left': 'o-ang-right',item.rate == 100 ? 'green' : item.rate == 0 ? 'gray' : 'blue']"></div><div:class="[display === 'right-only'? 'o-content-right': index % 2 === 1? 'o-content-left': 'o-content-right',item.rate == 100 ? 'green' : item.rate == 0 ? 'gray' : 'blue']":style="{'--width': display === 'right-only' ? '100%' : '50%'}"><div class="time"><span class="time-title">{{ item.leftTitle || '-' }}</span><span>{{ item.planTime }}~{{ item.finishTime }}</span><span>{{ item.desc || '--' }}</span></div><div class="o-content-text"><span>{{ item.title || '--' }}</span><span>{{ item.text || '--' }}</span></div></div></div></el-tooltip></div></div></div>
</template>
<script lang="ts" setup>
import { defineProps, toRefs } from 'vue'
import { ElTooltip } from 'element-plus'
import './style.less'const BLUE_COLOR = '#1890ff'
const GREEN_COLOR = '#52c41a'
interface listItem {leftTitle?: stringplanTime: stringfinishTime: stringdesc?: stringtitle: stringtext: stringratePercent: stringrate: number
}
interface IProps {width?: numberdotLeft: '50%' | '0%'display: 'left-and-right' | 'right-only'timelineDesc: listItem[]
}
const props = defineProps<IProps>()
const { timelineDesc, dotLeft } = toRefs(props)
</script>
<style lang="less" scoped>
@blue: #1890ff;
@green: #52c41a;
@gray: #aaa;
@white: #fff;
@black: #333;
@dotWidth: 10px;
@dotHeight: 10px;
@dotLeft: v-bind(dotLeft);
@tailBorderWidth: 3px;
@itemHeight: 80px;
.o-timeline-area {margin: 0 auto;.o-timeline {.o-timeline-item {position: relative;padding-bottom: 10px;height: @itemHeight;.o-tail {position: absolute;top: @dotHeight;left: calc(@dotLeft - @tailBorderWidth / 2);height: calc(100% - @dotHeight);border-left: @tailBorderWidth solid @gray;}.o-finish {position: absolute;top: @dotHeight;left: calc(@dotLeft - @tailBorderWidth / 2);height: calc((100% - @dotHeight) * var(--rate));border-left: @tailBorderWidth solid var(--borderColor);}.o-dot {position: absolute;width: @dotWidth;height: @dotHeight;left: calc(@dotLeft - @dotWidth / 2);display: flex;justify-content: center;align-items: center;}.solid-dot {width: 100%;height: 100%;border-radius: 50%;background-color: @gray;&.green {background-color: @green;}&.blue {background-color: @blue;}&.gray {background-color: @gray;}}.o-content {height: 100%;position: relative;top: -50%;left: calc(0px - @dotWidth / 2);display: flex;align-items: center;transform: translateY(calc(0px + @dotHeight / 2));}.o-content-left,.o-content-right {width: calc(var(--width) - @dotWidth);top: 8px;margin-left: 0px;word-break: break-all;word-wrap: break-word;font-size: 14px;font-weight: 400;background-color: rgba(@gray, 0.5);border-radius: 5px;padding: 5px 16px 10px;box-sizing: border-box;color: @gray;&.blue {background-color: rgba(@blue, 0.5);.time {color: @white;&-title {color: @black;}}}&.green {background-color: rgba(@green, 0.5);.time {color: @white;&-title {color: @black;}}}.time {display: inline-block;font-size: 14px;font-weight: 400;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;width: 100%;&-title {margin-right: 5px;}}&-text {display: flex;justify-content: space-between;}}.o-content-right {margin-left: calc(@dotLeft + @dotWidth);}.o-ang-left,.o-ang-right {display: block;width: 0;height: 0;border-width: 5px;border-style: solid;}.o-ang-left {position: relative;left: calc(@dotLeft + @dotWidth);border-color: transparent rgba(@gray, 0.5) transparent transparent;&.blue {border-color: transparent rgba(@blue, 0.5) transparent transparent;}&.green {border-color: transparent rgba(@green, 0.5) transparent transparent;}}.o-ang-right {position: relative;left: calc(@dotLeft - @dotWidth / 2);border-color: transparent transparent transparent rgba(@gray, 0.5);&.blue {border-color: transparent transparent transparent rgba(@blue, 0.5);}&.green {border-color: transparent transparent transparent rgba(@green, 0.5);}}}.last {.o-tail,.o-finish {display: none;}}}
}
</style>

调用该时间轴组件(描述内容只在右边显示):

<template><div class="o-main-box"><MyTimeLineCol :timelineDesc="timelineDesc" display="right-only" dotLeft="0%" /></div><div v-if="list.length > VISIBLE_NUM" class="operate" @click="handleClick">{{ flag == 'fold' ? '展开' : '收起' }}<van-icon v-if="flag == 'fold'" name="arrow-down" /><van-icon v-else-if="flag == 'open'" name="arrow-up" /></div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import MyTimeLineCol from './MyTimeLineCol.vue'
const VISIBLE_NUM = 2
const flag = ref('fold')
const visibleNum = ref<number>(VISIBLE_NUM)
const list = ref([{title: '启动',leftTitle: '标题1',planTime: '2019-01-01',finishTime: '2019-12-30',rate: 100,currentRate: 0,text: '100%',ratePercent: 1,desc: '已完成'},{title: '需求确认',leftTitle: '标题2',planTime: '2020-01-01',finishTime: '2020-12-30',rate: 60,currentRate: 0,text: '60%',ratePercent: 0.6,desc: '需求确认中'},{title: '项目开发',leftTitle: '标题3',planTime: '2021-01-01',finishTime: '2021-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0.0,desc: '未开始'},{title: '功能测试',leftTitle: '标题4',planTime: '2022-01-01',finishTime: '2022-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0,desc: '未开始'},{title: '上线',leftTitle: '标题5',planTime: '2023-01-01',finishTime: '2023-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0,desc: '未开始'}
])
const timelineDesc = computed(() => {return list.value.slice(0, visibleNum.value)
})
const handleClick = () => {if (flag.value === 'fold') {flag.value = 'open'visibleNum.value = list.value.length} else {flag.value = 'fold'visibleNum.value = VISIBLE_NUM}
}
</script>
<style lang="less" scoped>
.o-main-box {margin-top: 26px;
}
.operate {color: #9096a5;font-size: 12px;line-height: 20px;height: 20px;margin-top: 18px;text-align: center;cursor: pointer;
}
</style>

调用该时间轴组件(描述内容左右边岔开显示):

<template><div class="o-main-box"><MyTimeLineCol :timelineDesc="timelineDesc" display="left-and-right" dotLeft="50%" /></div><div v-if="list.length > VISIBLE_NUM" class="operate" @click="handleClick">{{ flag == 'fold' ? '展开' : '收起' }}<van-icon v-if="flag == 'fold'" name="arrow-down" /><van-icon v-else-if="flag == 'open'" name="arrow-up" /></div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import MyTimeLineCol from './MyTimeLineCol.vue'
const VISIBLE_NUM = 2
const flag = ref('fold')
const visibleNum = ref<number>(VISIBLE_NUM)
const list = ref([{title: '启动',leftTitle: '标题1',planTime: '2019-01-01',finishTime: '2019-12-30',rate: 100,currentRate: 0,text: '100%',ratePercent: 1,desc: '已完成'},{title: '需求确认',leftTitle: '标题2',planTime: '2020-01-01',finishTime: '2020-12-30',rate: 60,currentRate: 0,text: '60%',ratePercent: 0.6,desc: '需求确认中'},{title: '项目开发',leftTitle: '标题3',planTime: '2021-01-01',finishTime: '2021-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0.0,desc: '未开始'},{title: '功能测试',leftTitle: '标题4',planTime: '2022-01-01',finishTime: '2022-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0,desc: '未开始'},{title: '上线',leftTitle: '标题5',planTime: '2023-01-01',finishTime: '2023-12-30',rate: 0,currentRate: 0,text: '0%',ratePercent: 0,desc: '未开始'}
])
const timelineDesc = computed(() => {return list.value.slice(0, visibleNum.value)
})
const handleClick = () => {if (flag.value === 'fold') {flag.value = 'open'visibleNum.value = list.value.length} else {flag.value = 'fold'visibleNum.value = VISIBLE_NUM}
}
</script>
<style lang="less" scoped>
.o-main-box {margin-top: 26px;
}
.operate {color: #9096a5;font-size: 12px;line-height: 20px;height: 20px;margin-top: 18px;text-align: center;cursor: pointer;
}
</style>

http://www.dtcms.com/a/352207.html

相关文章:

  • 【python】python利用QQ邮箱SMTP发送邮件
  • k8s pod resources: {} 设置的含义
  • 支持向量机(第二十九节课内容总结)
  • TensorFlow 面试题及详细答案 120道(61-70)-- 高级特性与工具
  • 如何在项目中集成XXL-JOB
  • uniapp 引入使用u-view 完整步骤,u-view 样式不生效
  • 重复文件删除查找工具 Duplicate Files Search Link v10.7.0
  • 【深度学习】Transformer 注意力机制与 LoRA target_modules 详解
  • 如何安装 VS2019 和 .NET Core SDK 2.2.301(winx64)?完整操作步骤(附安装包下载)
  • 基于YOLOv11训练无人机视角Visdrone2019数据集
  • 区块链技术探索与应用:从密码学奇迹到产业变革引擎
  • 从入门到理解:支持向量机的核心原理与实战思路
  • 计数组合学7.21(有界部分大小的平面分拆)
  • 车载铁框矫平机:一辆“会熨衣服”的工程车
  • 高性能异步任务编排框架:Gobrs-Async
  • 【项目】深房数据通——深圳房价可视化系统
  • 嵌入式第三十七课!!!TCP机制与HTTP协议
  • 【学习笔记】系统时间跳变会影响time接口解决措施
  • 相关法律、法规知识(五)
  • 单层膜可改善无铅钙钛矿太阳能电池
  • Java 企业应用单点登录(SSO)实现方案详解
  • 创维桌面云终端-创维LB2002-白盒-晶晨S905L3A-2+8G-线刷刷机包
  • 实验2 天气预报
  • Ultra Accelerator Link(UALink)Consortium
  • 网站测试报告:WEB应用反CSRF的本质与防御机制
  • 解决 pdf.mjs 因 MIME 类型错误导致的模块加载失败问题
  • day1_线性回归的实现 李沐动手学深度学习pytorch记录
  • 吱吱企业通讯软件保障企业办公安全与效率,助力企业高效发展
  • (LeetCode 每日一题) 3000. 对角线最长的矩形的面积(数组)
  • Jmeter基础:Jmeter聚合报告