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), // 最小可見范圍 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>開始時(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>開始時(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è)父組件是通過點(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)用開發(fā)中,uni-app是一個(gè)非常流行的框架,它允許開發(fā)者使用一套代碼庫(kù)構(gòu)建多端應(yīng)用,包括H5、小程序、App等,這篇文章主要給大家介紹了關(guān)于uni-app中App與webview雙向?qū)崟r(shí)通信的相關(guān)資料,需要的朋友可以參考下2024-07-07vue實(shí)現(xiàn)購(gòu)物車功能(親測(cè)可用)
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)購(gòu)物車功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04el-tree樹組件懶加載(后端上千條數(shù)據(jù)前端進(jìn)行處理)
本文主要介紹了el-tree樹組件懶加載(后端上千條數(shù)據(jù)前端進(jìn)行處理),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03v-for中動(dòng)態(tài)校驗(yàn)el-form表單項(xiàng)的實(shí)踐
在項(xiàng)目開發(fā)中,我們經(jīng)常會(huì)遇到表單保存的功能,本文主要介紹了v-for中動(dòng)態(tài)校驗(yàn)el-form表單項(xiàng)的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2022-05-05