vue3使用vis繪制甘特圖制作timeline可拖動(dòng)時(shí)間軸及時(shí)間軸中文化(推薦)
前言:參考文檔文章
一、實(shí)現(xiàn)效果:

二、安裝插件及依賴:
cnpm install -S vis-linetime cnpm install -S vis-data cnpm install -S moment
三、封裝組件:
下端時(shí)間軸單獨(dú)封裝成組件


1.html部分:
<template> <div class="visGantt" ref="visGanttDom"></div> </template>
2.引入依賴:
import { DataSet } from 'vis-data/peer'
import { dateFormat } from '@/util' //封裝的時(shí)間格式化函數(shù),如下所示
import { Timeline } from 'vis-timeline/peer'
import 'vis-timeline/styles/vis-timeline-graph2d.css'
const moment = require('moment')時(shí)間格式化函數(shù):
export function dateFormat(date, fmt) { //date是日期,fmt是格式
let o = {
'M+': date.getMonth() + 1, // 月份
'd+': date.getDate(), // 日
'H+': date.getHours(), // 小時(shí)
'h+': date.getHours(), // 小時(shí)
'm+': date.getMinutes(), // 分
's+': date.getSeconds(), // 秒
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
}
for (var k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
}
}
return fmt
}3.父組件傳入數(shù)據(jù):
let props = defineProps({
ganntData: { // 初始傳入數(shù)據(jù)
type: Object,
default: () => {}
},
ganntHistoryData: { // 全部的歷史數(shù)據(jù),為了實(shí)現(xiàn)撤銷上一步
type: Object,
default: () => {}
}
})4.js部分全部配置
配置項(xiàng)參考官方文檔,僅注釋解釋個(gè)別方法。
<script setup>
import { ref, defineProps, watch, nextTick, defineEmits } from 'vue'
import { DataSet } from 'vis-data/peer'
import { dateFormat } from '@/util'
import { Timeline } from 'vis-timeline/peer'
import 'vis-timeline/styles/vis-timeline-graph2d.css'
const moment = require('moment')
let props = defineProps({
ganntData: {
type: Object,
default: () => {}
},
ganntHistoryData: {
type: Object,
default: () => {}
}
})
let timeline = ref(null)
watch(
props.ganntData,
(newVal) => {
if (newVal && newVal[0].trackTimeWindows && newVal[0].trackTimeWindows.length > 0) {
nextTick(() => {
initChart()
checkTimeConflict()
})
}
},
{
immediate: true,
deep: true
}
)
const computedData = () =>{
const trackTimeWindows = []
const timeWindows = []
props.ganntData[0].trackTimeWindows.forEach(
(trackTimeWindow, trackTimeWindowIndex) => {
// 項(xiàng)目類別數(shù)組
trackTimeWindows.push({
content: trackTimeWindow.deviceId,
id: `${trackTimeWindow.deviceId}-${trackTimeWindowIndex}-trackTimeWindows`,
value: trackTimeWindowIndex + 1,
className: `visGantt-item${trackTimeWindowIndex % 10}`
})
// 項(xiàng)目時(shí)間數(shù)組
trackTimeWindow.timeWindows.forEach((timeWindow, timeWindowIndex) => {
timeWindows.push({
start: new Date(timeWindow.startTime),
startTime: timeWindow.startTime,
end: new Date(timeWindow.stopTime),
stopTime: timeWindow.stopTime,
topTime: timeWindow.topTime,
group: `${trackTimeWindow.deviceId}-${trackTimeWindowIndex}-trackTimeWindows`,
className: `visGantt-item${trackTimeWindowIndex % 10}`,
id: `${trackTimeWindow.deviceId}`,
deviceId: trackTimeWindow.deviceId
})
})
}
)
return {
trackTimeWindows,
timeWindows
}
}
const visGanttDom = ref(null)
let historyDataArray = ref([])
const emit = defineEmits()
// 選擇某個(gè)item
let onSelect = (properties) => {
emit('selectItem', properties.items[0])
}
const initChart = ()=> {
const { timeWindows, trackTimeWindows } = computedData()
const groups = new DataSet(trackTimeWindows)
const items = new DataSet(timeWindows)
let container = visGanttDom.value
if (container.firstChild) {
container.removeChild(container.firstChild)
}
// 甘特圖配置
const options = {
groupOrder: function(a, b) {
return a.value - b.value
},
groupOrderSwap: function(a, b, groups) {
var v = a.value
a.value = b.value
b.value = v
},
height: '23.8vh', // 高度
verticalScroll: false, // 豎向滾動(dòng)
orientation: 'top', // 時(shí)間軸位置
margin: {
axis: 1,
item: {
horizontal: 0,
vertical: 20
}
},
editable: {
updateTime: true,
updateGroup: false
},
multiselect: true,
moment: function(date) {
return moment(date).utc('+08:00')
},
groupHeightMode: 'fixed',
// min: new Date(startTime.value), // 最小可見(jiàn)范圍
tooltip: {
followMouse: true,
overflowMethod: 'cap',
template: (originalItemData, parsedItemData) => {
// 鼠標(biāo)hover時(shí)顯示樣式
return `<div>
<p>
<span>項(xiàng)目名稱:</span>
<span>${originalItemData.deviceId}</span>
</p><br/>
<p>
<span>開(kāi)始時(shí)間:</span>
<span>${dateFormat(parsedItemData.start, 'yyyy-MM-dd')}</span>
</p><br/>
<span>結(jié)束時(shí)間:</span>
<span>${dateFormat(parsedItemData.end, 'yyyy-MM-dd')}</span>
</p>
</div>`
}
},
tooltipOnItemUpdateTime: {
template: (item) => {
// 鼠標(biāo)拖動(dòng)時(shí)顯示樣式
return `<div>
<span>開(kāi)始時(shí)間:${dateFormat(item.start, 'yyyy-MM-dd')}</span>
<span>\n</span>
<span>結(jié)束時(shí)間:${dateFormat(item.end, 'yyyy-MM-dd')}</span>
</div>`
}
},
locale: moment.locale('zh-cn'), // 時(shí)間軸國(guó)際化
showCurrentTime: false,
selectable: true,
zoomMin: 1728000000,
zoomMax: 315360000000,
// showTooltips: false,
// autoResize: false,
snap: function(date, scale, step) {
var day = 60 * 60 * 1000 * 24
return Math.round(date / day) * day
},
// 移動(dòng)時(shí)返回函數(shù)
onMove: function(item, callback) {
// 深拷貝一下,不能直接修改父組件數(shù)據(jù)
historyDataArray.value = JSON.parse(JSON.stringify(props.ganntHistoryData))
let historyData = []
// props.ganntHistoryData是全部的歷史數(shù)據(jù),historyData 是取上一步的數(shù)據(jù)
historyData = JSON.parse(JSON.stringify(props.ganntHistoryData[props.ganntHistoryData.length - 1]))
// 更改一下格式
historyData[0].trackTimeWindows.forEach((eachItem)=>{
if (eachItem.deviceId === item.deviceId) {
if (!item.start || !item.end) {
return
}
eachItem.timeWindows[0].startTime = item.start
eachItem.timeWindows[0].stopTime = item.end
}
})
historyDataArray.value.push(historyData)
// 更新一下ganntHistoryData歷史數(shù)據(jù)
emit('update:ganntHistoryData', historyDataArray.value)
callback(item)
},
onMoving: function(item, callback) {
// 移動(dòng)時(shí)間軸時(shí)不顯示tooltip提示框
let tooltipDom = document.getElementsByClassName('vis-tooltip')
tooltipDom[0].style.visibility = 'hidden'
callback(item)
}
}
timeline.value = new Timeline(container)
timeline.value.redraw()
timeline.value.setOptions(options)
timeline.value.setGroups(groups)
timeline.value.setItems(items)
timeline.value.on('select', onSelect)
}
</script>
四、父組件調(diào)用
1.引入子組件
<div v-loading="loading"> // loading是為了有個(gè)加載效果,為了美觀 <time-line :ganntData="ganntData" // 原始數(shù)據(jù) v-model:ganntHistoryData="ganntHistoryData" // 歷史數(shù)據(jù) @selectItem="timelineSelected" //選擇事件 > </time-line> </div>
import TimeLine from '@/components/modules/planControl/TimeLine'
2.初始數(shù)據(jù)
let props = defineProps({
// 因?yàn)檫@個(gè)父組件是通過(guò)點(diǎn)擊進(jìn)來(lái)的,所以有傳入的數(shù)據(jù),也可以直接賦值ganntData 數(shù)據(jù),可以省略watch里面的轉(zhuǎn)格式
conflictList: {
type: Array,
default: null
}
})
const ganntData = reactive([
{
name: 'confilct',
trackTimeWindows: [
]
}
])
const ganntHistoryData = ref([])
// 傳入數(shù)據(jù)變化時(shí)為ganntData和ganntHistoryData賦值。
watch(
() => props.conflictList, (newValue) => {
ganntData[0].trackTimeWindows.length = 0
newValue.forEach(element => {
ganntData[0].trackTimeWindows.push({
deviceId: element.content,
timeWindows: [
{
startTime: element.startTime,
stopTime: element.stopTime
}
]
})
})
// 記錄操作歷史
ganntHistoryData.value.length = 0
ganntHistoryData.value.push(ganntData)
},
{
deep: true,
immediate: true
}
)
原數(shù)據(jù)(省略部分未使用參數(shù)):
[
{
"id": 1,
"content": "xxxxxxxxxxxxxx計(jì)劃1",
"time": "2023.08~10",
"startTime": "2023-08-09",
"stopTime": "2023-10-20"
},
{
"id": 2,
"content": "xxxxxxxxxxxxxx計(jì)劃2",
"time": "2023.09~11",
"startTime": "2023-09-09",
"stopTime": "2023-11-1"
},
{
"id": 3,
"content": "xxxxxxxxxxxxxx計(jì)劃3",
"time": "2023.08~10",
"startTime": "2023-08-20",
"stopTime": "2023-10-1"
}
]3.父組件按鈕及事件
僅展示原始圖、撤銷事件。
<div>
<div>
<el-button @click="reset()">原始圖</el-button>
<el-button @click="preNode()">撤銷</el-button>
<el-button>一鍵調(diào)整</el-button>
</div>
<div>
<el-button>取消</el-button>
<el-button>保存并退出</el-button>
</div>
</div>回歸原始圖事件:
大致思路:先把ganntData清空,將拿到的props.conflictList里的數(shù)據(jù)賦值給ganntData,再把ganntData的數(shù)據(jù)push進(jìn)ganntHistoryData中
// showResetTip 是顯示一個(gè)“已回到初始狀態(tài)”的提示框,可以自己封裝或者使用組件,此處不展示
const showResetTip = ref(false)
const loading = ref(false)
const reset = () => {
// loading是加載效果
loading.value = true
ganntData[0].trackTimeWindows.length = 0
props.conflictList.forEach(element => {
ganntData[0].trackTimeWindows.push({
deviceId: element.content,
timeWindows: [
{
startTime: element.startTime,
stopTime: element.stopTime
}
]
})
})
showResetTip.value = true
ganntHistoryData.value.splice(0)
ganntHistoryData.value.push(ganntData)
setTimeout(() => {
showResetTip.value = false
loading.value = false
}, 1000)
}撤銷事件:
大致思路:拿到子組件返回的ganntHistoryData歷史數(shù)據(jù)數(shù)組,刪掉最后一組數(shù)據(jù)后:
如果歷史數(shù)據(jù)數(shù)組的長(zhǎng)度<= 1,代表再撤銷就回到原始狀態(tài)了,那就直接調(diào)用reset()回到原始圖;
否則,將ganntHistoryData刪掉最后一組數(shù)據(jù)后的ganntHistoryDataClone最后一組值賦給ganntData,
const preNode = () => {
// loading是加載效果
loading.value = true
let ganntHistoryDataClone = []
ganntHistoryDataClone = JSON.parse(JSON.stringify(ganntHistoryData.value))
ganntHistoryDataClone.splice(ganntHistoryDataClone.length - 1, 1)
if (ganntHistoryDataClone.length <= 1) {
reset()
} else {
ganntData[0] = ganntHistoryDataClone[ganntHistoryDataClone.length - 1][0]
ganntHistoryData.value = JSON.parse(JSON.stringify(ganntHistoryDataClone))
}
setTimeout(() => {
loading.value = false
}, 1000)
}到此這篇關(guān)于vue3使用vis繪制甘特圖制作timeline可拖動(dòng)時(shí)間軸,時(shí)間軸中文化的文章就介紹到這了,更多相關(guān)vue3用vis繪制甘特圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
uni-app中App與webview雙向?qū)崟r(shí)通信詳細(xì)代碼示例
在移動(dòng)應(yīng)用開(kāi)發(fā)中,uni-app是一個(gè)非常流行的框架,它允許開(kāi)發(fā)者使用一套代碼庫(kù)構(gòu)建多端應(yīng)用,包括H5、小程序、App等,這篇文章主要給大家介紹了關(guān)于uni-app中App與webview雙向?qū)崟r(shí)通信的相關(guān)資料,需要的朋友可以參考下2024-07-07
vue實(shí)現(xiàn)購(gòu)物車功能(親測(cè)可用)
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)購(gòu)物車功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
el-tree樹(shù)組件懶加載(后端上千條數(shù)據(jù)前端進(jìn)行處理)
本文主要介紹了el-tree樹(shù)組件懶加載(后端上千條數(shù)據(jù)前端進(jìn)行處理),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
v-for中動(dòng)態(tài)校驗(yàn)el-form表單項(xiàng)的實(shí)踐
在項(xiàng)目開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到表單保存的功能,本文主要介紹了v-for中動(dòng)態(tài)校驗(yàn)el-form表單項(xiàng)的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2022-05-05

