因为工作中要用到甘特图,所以我在网上搜索可以用的甘特图,搜索了好多,但是网上搜到大多数都很鸡肋,不能直接使用,最后我在github上搜索到一个相对成熟的甘特图。附上链接https://github.com/w1301625107/Vue-Gantt-chart
刚开始我看不懂这个博主写的代码,于是想自己写,但是尝试了几天,最后还是以失败告终。没办法活是我自己的,总得想办法弄出来啊,所以我硬着头皮改造代码。先附上博主的甘特图,我感觉这是我见过颜值最高的甘特图了。
因为需求要求做到可拖拽,以下是我实现拖拽的过程。
JS部分
首先介绍第一个mousedown,当我按下鼠标,我会记录当前块的索引。draggable设为可拖拽。
接着介绍drop,也就是移动到该行触发的事件,这里我用val传参(拖动到指定行的数据)和所有数据对比较,获取该行的索引,接着我会判断拖动的块与该行的块是否冲突。
附上源码:
<template>
<div class="gantt-blocks"
:style="{height:blockHeight+'px'}">
<div class="gantt-block gantt-block-top-space"
:style="{height:topSpace+'px'}">
</div>
<div class="gantt-block"
:style="blockStyle"
v-for="(data,index) in showDatas"
@drop="drop($event,data,index)"
@dragover.prevent
@click="clickRow(index)"
:class="[index === rowIndex ? 'rowColor':'']"
:key="dataKey?data[dataKey]:index">
<!-- 整个数据列表-->
<template v-if="!customGenerateBlocks">
<!-- 行-->
<div class="gantt-block-item block"
@mousedown="mousedown(data,item,index)"
draggable="true"
@contextmenu="showMenu(data,item,index)"
v-for="(item,index) in concatArray(data)"
v-show="isInRenderingTimeRange(item.start)||isInRenderingTimeRange(item.end)"
:key="itemKey?item[itemKey]:index"
:class="[blockState && (item === data.gtArray[operationIndex]) ? groundFloor : superstratum]"
:style="{left:getPosition(item)+'px',width:getWidth(item)+'px'}">
<slot :data="data"
:item="item">
<div class="gantt-block-defaultBlock">need slot</div>
</slot>
</div>
</template>
<template v-else>
<slot :data="data"
:getPositonOffset="getPositonOffset"
:getWidthAbout2Times="getWidthAbout2Times"
:isInRenderingTimeRange="isInRenderingTimeRange">need slot</slot>
</template>
</div>
</div>
</template>
<script>
import dayjs from "dayjs";
import dr from "../dynamic-render.js";
import { isUndef, warn } from "../../utils/tool.js";
export default {
name: "Blocks",
mixins: [dr],
data () {
return {
handleIndex:0,//行角标
handleItem:{},
handleData:{},//当前行数据
dataCorner:0,//当前行角标
dialogVisible: false,
content: {},
blockIndex: -1,//行内航班所在行角标
operationIndex:null,
blockState:false,
groundFloor:'groundFloor',
superstratum:'superstratum'
}
},
props: {
dataKey: String,
itemKey: String,
arrayKeys: {
type: Array
},
scrollLeft: Number,
cellWidth: {
type: Number,
required: true
},
scale: {
type: Number,
required: true
},
widthOfRenderAera: {
type: Number,
required: true
},
endTimeOfRenderArea: [Number, null],
startTimeOfRenderArea: [Number, null],
getPositonOffset: Function,
getWidthAbout2Times: Function,
customGenerateBlocks: Boolean,
editStart: {
type:Date
},
editEnd: {
type:Date
},
state:{
type:Boolean
},
uninstall:{
type:Boolean
},
rowIndex: {
type:Number
}
},
computed: {
renderAarrys() {
let { arrayKeys } = this;
if (arrayKeys.length > 0) {
return arrayKeys;
}
return ["gtArray"];
},
blockStyle() {
return {
backgroundSize: `${this.cellWidth}px ${this.cellHeight}px`,
height: `${this.cellHeight}px`
};
}
},
watch:{
state () {
if(this.state){
if((Number(dayjs(this.editStart).valueOf())>=Number(this.showDatas[this.dataCorner].gtArray[this.blockIndex-1].end)) &&
(Number(dayjs(this.editEnd).valueOf())<=Number(this.showDatas[this.dataCorner].gtArray[this.blockIndex+1].start))) {
this.showDatas[this.dataCorner].gtArray[this.blockIndex].start = dayjs(this.editStart).valueOf()
this.showDatas[this.dataCorner].gtArray[this.blockIndex].end = dayjs(this.editEnd).valueOf()
} else {
this.$emit('creatDialog', true)
}
}
},
uninstall () {
if (this.uninstall) {
this.showDatas[0].gtArray.push(this.handleItem)
this.showDatas[this.dataCorner].gtArray.splice(this.handleIndex, 1)
let arr = []
arr = this.showDatas[0].gtArray
this.$emit('changeUninstall', false, arr)
}
this.$emit('detailsItem', this.handleItem)
}
},
methods: {
/**
* 根据renderAarrys拼接需要渲染的数组
*
* @param {*} data
* @returns {[]} 该data中所有需要渲染的数据
*/
concatArray(data) {
return this.renderAarrys.reduce((prev, curr) => {
if (Array.isArray(data[curr])) {
return prev.concat(data[curr]);
} else {
return prev;
}
}, []);
},
/**
* 判定数据是否在渲染的时间范围内
*
* @param {{time:string}} item
* @returns {boolean} 该
*/
isInRenderingTimeRange(time) {
if (this.heightOfRenderAera === 0) {
return false;
}
let { startTimeOfRenderArea, endTimeOfRenderArea } = this;
if (isUndef(startTimeOfRenderArea) || isUndef(endTimeOfRenderArea)) {
return false;
}
let timeToMs = new Date(time).getTime();
if (startTimeOfRenderArea <= timeToMs || timeToMs >= endTimeOfRenderArea) {
return true;
}
return false;
},
/**
* 计算时间块长度
*
* @param {{start:string,end:string}} block
* @returns {number}
*/
getWidth(block) {
if (isUndef(block.start) || isUndef(block.end)) {
// warn(`错误,该数据项不含start值 与 end 值 ${JSON.stringify(block)},无法计算宽度值。`)
return 0;
}
return this.getWidthAbout2Times(block.start, block.end);
},
/**
* 计算时间块偏移
*
* @param {{start:string}} block
* @returns {number}
*/
getPosition(block) {
if (isUndef(block.start)) {
warn(
`错误,该数据项不含start 值 ${JSON.stringify(
block
)},无法计算偏移值。`
);
return 0;
}
return this.getPositonOffset(block.start);
},
mousedown (data,item, index) {
this.$emit('showTime', true)
this.handleItem = item
this.handleData = data
this.blockIndex = index
for(let i = 0;i<this.showDatas.length;i++) {
if(this.showDatas[i] === data){
this.dataCorner = i
}
}
this.$emit('nowRow', this.dataCorner)
},
drop (event) {
console.log('event.y', event,Math.ceil((event.y-100)/this.cellHeight-1)-1)
var endy=Math.ceil((event.y-100)/this.cellHeight-1)-1;
let data = this.showDatas[endy].gtArray
//判断是否重叠(不重叠)
let blockStart = Number(this.handleItem.start)
let blockEnd = Number(this.handleItem.end)
let x = 0
if (Number(endy) === 0) {
this.$emit('creatState', true)
} else {
for(let i=0; i < data.length; i++) {
if (((blockEnd >= Number(data[i].end)) && (blockStart >= Number(data[i].end))) ||
((blockEnd <= Number(data[i].start)) && (blockStart <= Number(data[i].start)))) {
++x
}
}
if(x === data.length) {
data.push(this.handleItem)
this.showDatas[this.dataCorner].gtArray.splice(this.blockIndex, 1)
this.$emit('nowRow', endy)
console.log('this.dataCorner', this.dataCorner, 'this.handleIndex', this.handleIndex)
} else {
this.$emit('creatDialog', true)
}
}
},
newData () {
console.log('newdata!',this.dataCorner,this.blockState)
this.operationIndex = this.blockIndex
this.blockState = true
},
clickRow (data) {
this.$emit('nowRow', data)
}
}
};
</script>
<style lang="scss">
.block{
cursor: move;
position: fixed;
}
.groundFloor{
z-index: 0;
position:absolute;
}
.superstratum{
z-index: 1;
position:absolute;
}
.rowColor{
background-color: #edf3ef;
}
.creatColor{
background-color: #dfe5e1 !important;
}
</style>
以上就是我的分享,如果有什么问题,欢迎大家留言。
最后附上我上传的源码,如有需要可自行下载,链接: 甘特图链接.