使用vis-timeline 完成时间轴事件追踪表

效果如图所示:

首先是装包

csharp 复制代码
    pnpm add vis-timeline moment

引入

javascript 复制代码
import { DataSet, Timeline } from 'vis-timeline/standalone'
import moment from 'moment'

import 'moment/dist/locale/zh-cn.js'
import 'vis-timeline/styles/vis-timeline-graph2d.min.css'

这里赘述一下 moment的作用是将语言国际化处理

紧接着是option的配置

yaml 复制代码
  const options = {
    start: new Date(2021, 1, 1), 
    orientation: 'bottom',
    showCurrentTime: true,
    zoomable: false,
    moveable: true,
    stack: false,
    margin: {
      item: 20,
      axis: 20,
    },
    type: 'box',
    showMajorLabels: true,
    dataAttributes: 'all',
    locale: 'zh-cn',
    moment (date) {
      return moment(date).utc() // 国际化处理
    },
  }

接着配置 groups items

arduino 复制代码
  const items = new DataSet(itemsData)
  const groups = new DataSet(groupsData)

数据配置预备好之后 创建实例

arduino 复制代码
const timeline = new Timeline(timelineContainer.value, items, groups, options)

我们这里的需求还要求有点击图标展示弹框 所以还加了弹框的逻辑

javascript 复制代码
  timeline.on('select', (props) => {
    if (props.items.includes('qs')) {
      return pdfVisible.value = true
    }
    if (props.items.length > 0) {
      visible.value = true
    }
  })

最后为了还原UI图效果 修改了一系列的样式;这里学到一个小tips:在scss里面可以用@extend来继承样式;ok到最后贴下整个文件的代码!

xml 复制代码
<script setup>
import { Close } from '@element-plus/icons-vue'

import moment from 'moment'
import { DataSet, Timeline } from 'vis-timeline/standalone'
import { onMounted, ref } from 'vue'
import 'moment/dist/locale/zh-cn.js'
import 'vis-timeline/styles/vis-timeline-graph2d.min.css'

const timelineContainer = ref(null)
const visible = ref(false)
const pdfVisible = ref(false)

onMounted(() => {
  // 事件
  const itemsData = [
    { id: 'qs', content: '', start: '2021-2-15', group: '权属', className: 'diamond-qs' },
    { id: 'ly', content: '', start: '2021-09-01', group: '利用', className: 'diamond-ly' },
    { id: 'zs', content: '', start: '2022-07-01', group: '征收', className: 'diamond-zs' },
    { id: 'xg', content: '', start: '2023-09-01', group: '详规', className: 'diamond-xg' },
  ]

  // 分组
  const groupsData = [
    { id: '权属', content: '权属', className: 'group-diamond' },
    { id: '利用', content: '利用', className: 'group-diamond' },
    { id: '征收', content: '征收', className: 'group-diamond' },
    { id: '详规', content: '详规', className: 'group-diamond' },
  ]

  const items = new DataSet(itemsData)
  const groups = new DataSet(groupsData)

  const options = {
    start: new Date(2021, 1, 1),
    orientation: 'bottom',
    showCurrentTime: true,
    zoomable: false,
    moveable: true,
    stack: false,
    margin: {
      item: 20,
      axis: 20,
    },
    type: 'box',
    showMajorLabels: true,
    dataAttributes: 'all',
    locale: 'zh-cn',
    moment (date) {
      return moment(date).utc()
    },
  }

  const timeline = new Timeline(timelineContainer.value, items, groups, options)

  timeline.on('select', (props) => {
    if (props.items.includes('qs')) {
      return pdfVisible.value = true
    }
    if (props.items.length > 0) {
      visible.value = true
    }
  })
})
</script>

<template>
  <div
    ref="timelineContainer"
    class="timeline-container"
  />
  <!-- 详情弹框 -->
  <div
    v-if="visible"
    class="timeline-popup"
  >
    <div class="timeline-popup-header">
      <div>详情</div>
      <el-icon
        class="cursor-pointer"
        @click="visible = false"
      >
        <Close />
      </el-icon>
    </div>
    <div class="timeline-popup-content">
      <div>农转用批文号:浙土字</div>
      <div>农转用总面积:133.56公顷</div>
      <div>重叠面积:22.13公顷</div>
    </div>
  </div>
  <!-- pdf预览弹框 -->
  <Teleport to=".box-content">
    <ElDialog
      v-model="pdfVisible"
      title="PDF预览"
    >
      <iframe
        src="path/to/your/file.pdf"
        width="100%"
        height="600px"
      />
    </ElDialog>
  </Teleport>
</template>

<style scoped lang="scss">
.diamond-common-yellow {
  background-image: linear-gradient(180deg, #FFE05E 0%, #E69319 100%);
  box-shadow: 0 2px 1px 0 rgba(255, 224, 94, 0.3);
  transform: rotate(45deg);
}
.diamond-common-red {
  background-image: linear-gradient(180deg, #FFA35E 0%, #E63C19 100%);
  box-shadow: 0 2px 1px 0 rgba(255, 131, 94, 0.3);
  transform: rotate(45deg);
}
.diamond-common-blue {
  background-image: linear-gradient(180deg, #5EFFF6 0%, #199CE6 100%);
  box-shadow: 0 2px 1px 0 rgba(94, 182, 255, 0.3);
  transform: rotate(45deg);
}
.diamond-common-green {
  background-image: linear-gradient(180deg, #81D87C 0%, #19E638 100%);
  box-shadow: 0 2px 1px 0 rgba(94, 255, 200, 0.3);
  transform: rotate(45deg);
}

.timeline-container {
  padding: 20px;
  position: relative;
}
:deep(.vis-timeline){
  border:none;
}
:deep(.vis-panel.vis-center){
  border-top:none;
  border-right:none;
  border-bottom:1px solid rgba(0, 176, 255, 0.34);
  border-left:1px solid rgba(0, 176, 255, 0.34);
}
:deep(.vis-labelset .vis-label){
  border:none;
}
:deep(.vis-panel.vis-bottom){
  border-right: none;
  border-left: none;
}
:deep(.vis-panel.vis-left){
  border-bottom:1px solid rgba(0, 176, 255, 0.34);
  border-top:none;
}
:deep(.vis-foreground .vis-group){
  border-bottom:dashed rgba(0, 176, 255, 0.3) 0.5px;
}
:deep(.vis-time-axis .vis-grid.vis-minor){
  border:dashed rgba(0, 176, 255, 0.3) 0.5px;
}
:deep(.vis-time-axis .vis-text){
  color:rgba(255, 255, 255, 0.4);
}
:deep(.vis-time-axis .vis-text.vis-major){
  font-weight: 500;
  font-size: 16px;
  color: #FFFFFF;
}
:deep(.vis-labelset .vis-label .vis-inner){
  font-family: AlibabaPuHuiTi_3_75_SemiBold;
  font-weight: 600;
  font-size: 16px;
  color: #FFC557;
  &::before{
    content: '';
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-right: 8px;
    @extend .diamond-common-yellow;
  }
}
:deep(.vis-labelset .vis-label:nth-of-type(2) .vis-inner){
  color:rgba(247, 96, 62, 1);
  &::before{
   @extend .diamond-common-red;
  }
}
:deep(.vis-labelset .vis-label:nth-of-type(3) .vis-inner){
  color:rgba(86, 210, 241, 1);
  &::before{
   @extend .diamond-common-blue;
  }
}
:deep(.vis-labelset .vis-label:nth-of-type(4) .vis-inner){
  color:rgba(75, 223, 89, 1);
  &::before{
    @extend .diamond-common-green;
  }
}
:deep(.vis-item.vis-dot),:deep(.vis-item.vis-box){
  background-color: transparent;
  border-color: transparent;
  width: 12px;
  height: 12px;
  cursor: pointer;
}
:deep(.vis-item.vis-point.vis-selected){
 background-color: transparent;
}
:deep(.diamond-qs .vis-item-content){
   @extend .diamond-common-yellow;
}
:deep(.diamond-ly .vis-item-content){
   @extend .diamond-common-red;
}
:deep(.diamond-zs .vis-item-content){
   @extend .diamond-common-blue;
}
:deep(.diamond-xg .vis-item-content){
   @extend .diamond-common-green;
}
.timeline-popup{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 258px;
  height: 128px;
  background-image: linear-gradient(180deg, rgba(33, 171, 243, 0.4) 0%, rgba(0, 100, 182, 0.4) 100%);
  border: 1px solid rgba(156, 241, 255, 0.27);
  border-radius: 4px;
  color: white;
  .timeline-popup-header{
    border-bottom:solid 1px rgba(33, 171, 243, 1);
    font-weight: 400;
    font-size: 16px;
    color: #FFFFFF;
    padding: 0 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .timeline-popup-content{
    padding: 10px 15px;
  }
}
.pdf-popup{

}
</style>

贴下网上找的中文文档blog.csdn.net/weixin_3830...

相关推荐
Csvn12 小时前
🧩「找不到模块」排查全记录——Monorepo 下 TypeScript 路径别名的 5 种「不通」与根治方案
前端
腻害兔12 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
CodexDave12 小时前
MySQL事务隔离级别与MVCC机制解析
前端·数据库·mysql·nginx·性能优化·负载均衡
Ai_easygo13 小时前
AI Agent开发入门——从ReAct到Tool Calling,拆解Agent的底层运行逻辑
前端·人工智能·react.js
研☆香13 小时前
分析制作html页面,如何划分页面结构
前端
腻害兔13 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
kyriewen14 小时前
我扒了最近的前端面经——2026年面试不背八股文了,考这5样
前端·面试·ai编程
IT_陈寒14 小时前
Redis的持久化配置把我坑惨了:你以为数据安全了?
前端·人工智能·后端
小徐_233314 小时前
uni-app 项目别再从零搭了!3 个 Wot UI 起手模板怎么选?
前端·uni-app
赵庆明老师15 小时前
Vben精讲:16-熟悉Vite前端工具链
前端