1460 lines
41 KiB
Python
1460 lines
41 KiB
Python
<template>
|
||
<div class="chart" ref="chart">
|
||
<div class="left" :style="{ width: rightLineX + 'px' }">
|
||
<leftMenu
|
||
:list="list"
|
||
ref="leftMenu"
|
||
:BGScrollTop.sync="BGScrollTop"
|
||
@TableScrollTop="TableScrollTop"
|
||
@handlerRowClick="handlerRowClick"
|
||
@handlerGroup="handlerGroup"
|
||
@handlerExpand="handlerExpand"
|
||
></leftMenu>
|
||
<div class="rightLine" :style="{ left: rightLineX + 'px' }"></div>
|
||
<div
|
||
class="rightLine"
|
||
:style="{ left: rightLineX + 'px' }"
|
||
ref="rightLine"
|
||
@mousedown="rightLineMousedown"
|
||
></div>
|
||
</div>
|
||
<div class="date" :style="{ left: rightLineX + 2 + 'px' }">
|
||
<div class="years" v-for="item in allDays" :key="item.year">
|
||
<div
|
||
class="month"
|
||
v-for="(value, key) in item.month[0]"
|
||
:key="value + 'zz' + key"
|
||
:style="{ width: value.length * currentDaySize.value + 'px' }"
|
||
>
|
||
<div class="month-top">{{ item.year }}年{{ key }}月</div>
|
||
</div>
|
||
</div>
|
||
<div class="topMonth" v-if="showFixdTopMonth">{{ fixdTopMonth }}</div>
|
||
<div class="allDaysArray">
|
||
<div class="alldays">
|
||
<div
|
||
v-for="(j, index) in days"
|
||
:key="index"
|
||
class="day"
|
||
:style="{ width: currentDaySize.value + 'px' }"
|
||
>
|
||
<template v-if="currentDaySize.value === 20">
|
||
<span
|
||
v-if="j.today"
|
||
class="dateNum todayDateNum"
|
||
style="border-left:1px solid #d7d7d7;"
|
||
>
|
||
今天
|
||
</span>
|
||
<span
|
||
v-else
|
||
class="dateNum"
|
||
:style="{
|
||
borderLeft: index == 0 ? 'none' : '1px solid #d7d7d7'
|
||
}"
|
||
:class="{
|
||
weekday: j.weekday == 0 || j.weekday == 6,
|
||
isHover:
|
||
j.width >= currentLineDay.start &&
|
||
j.width <= currentLineDay.end
|
||
}"
|
||
>{{ j.date }}</span>
|
||
<span
|
||
class="dateBG"
|
||
:class="{
|
||
weekday2: j.weekday == 0,
|
||
weekday1: j.weekday == 6,
|
||
today: j.today
|
||
}"
|
||
:style="{
|
||
width: currentDaySize.value + 'px',
|
||
height:
|
||
j.weekday == 0 || j.weekday == 6 ? lineBGHeight : '0px'
|
||
}"
|
||
></span>
|
||
</template>
|
||
<template v-if="currentDaySize.value == 10">
|
||
<span
|
||
class="dateNum"
|
||
:class="{
|
||
weekday: j.weekday == 0 || j.weekday == 6,
|
||
isHover:
|
||
j.width >= currentLineDay.start &&
|
||
j.width <= currentLineDay.end,
|
||
nodBorder:
|
||
j.width >= currentLineDay.start &&
|
||
j.width <= currentLineDay.end
|
||
}"
|
||
>
|
||
<div
|
||
v-show="
|
||
(j.width === currentLineDay.end &&isHover &&j.weekday !== 1) ||
|
||
(j.width === currentLineDay.start &&isHover &&j.weekday !== 1) ||
|
||
j.weekday === 1
|
||
"
|
||
style="width:100%;height:100%;"
|
||
:style="{
|
||
borderLeft: index === 0 ? 'none' : '1px solid #d7d7d7'
|
||
}"
|
||
>
|
||
{{ j.date }}
|
||
</div>
|
||
</span>
|
||
<span
|
||
class="dateBG"
|
||
:class="{
|
||
weekday2: j.weekday === 0,
|
||
weekday1: j.weekday === 6,
|
||
today: j.today
|
||
}"
|
||
:style="{
|
||
width: currentDaySize.value + 'px',
|
||
height:
|
||
j.weekday === 0 || j.weekday === 6 ? lineBGHeight : '0px'
|
||
}"
|
||
></span>
|
||
</template>
|
||
<template v-if="currentDaySize.value === 2">
|
||
<span
|
||
class="dateNum"
|
||
:class="{
|
||
isHover:
|
||
j.width >= currentLineDay.start &&
|
||
j.width <= currentLineDay.end,
|
||
nodBorder:
|
||
j.width >= currentLineDay.start &&
|
||
j.width <= currentLineDay.end
|
||
}"
|
||
>
|
||
<div
|
||
v-show="
|
||
(j.width === currentLineDay.end && isHover && j.date !== 1) ||
|
||
(j.width === currentLineDay.start &&isHover &&j.date !== 1) ||
|
||
j.date === 1
|
||
"
|
||
style="width:100%;height:100%;font-size:10px!important;"
|
||
:style="{
|
||
borderLeft: index === 0 ? 'none' : '1px solid #d7d7d7'
|
||
}"
|
||
>
|
||
{{ j.date }}
|
||
</div>
|
||
</span>
|
||
<span
|
||
class="dateBG weekday2"
|
||
:class="{
|
||
today: j.today
|
||
}"
|
||
style="border-right:none;"
|
||
:style="{
|
||
width: currentDaySize.value + 'px',
|
||
height:
|
||
j.weekday === 0 || j.weekday === 6 ? lineBGHeight : '0px'
|
||
}"
|
||
></span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<div class="lineBG" @scroll="handlerBGScroll" ref="lineBG">
|
||
<template v-for="(item, index) in computedList">
|
||
<!--<div :key="item.id" class="tetst">{{item.id}}</div>-->
|
||
<div
|
||
:ref="'line' + item.id"
|
||
:key="item.id + index"
|
||
class="line"
|
||
:style="{
|
||
left: item.left + 'px',
|
||
width: item.widthMe + 'px',
|
||
top: item.top + 'px'
|
||
}"
|
||
@mouseover="
|
||
lineMouseover(
|
||
`line${item.id}`,
|
||
$event,
|
||
item.id,
|
||
item.parentId,
|
||
index
|
||
)
|
||
"
|
||
@mouseleave="lineMouseleave"
|
||
@mouseenter="
|
||
lineMouseenter(
|
||
`line${item.id}`,
|
||
$event,
|
||
item.id,
|
||
item.parentId,
|
||
index
|
||
)
|
||
"
|
||
>
|
||
<slider
|
||
v-show="item.type == 1"
|
||
:id="item.id"
|
||
v-model="item.per"
|
||
:min="0"
|
||
:max="item.per"
|
||
:per1="item.per1"
|
||
:disabled="disable"
|
||
:widths="item.widthChild"
|
||
></slider>
|
||
</div>
|
||
<div
|
||
v-if="item.type == 3"
|
||
:key="item.id"
|
||
class="group"
|
||
:style="{
|
||
top: item.top + 'px',
|
||
left: item.left + 'px',
|
||
width: item.widthMe + 'px'
|
||
}"
|
||
>
|
||
<div class="progress" :style="{ width: item.per + '%' }"></div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<div class="toolTip">
|
||
<!--<div class="today base" @click="handleGoToday">今天</div>-->
|
||
<el-dropdown trigger="click">
|
||
<span class="base">
|
||
{{this.currentDaySize.label}}
|
||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||
</span>
|
||
<el-dropdown-menu
|
||
slot="dropdown"
|
||
:style="{ left: this.left + 'px !important' }"
|
||
>
|
||
<el-dropdown-item
|
||
v-for="item in currentDaySizeOptions"
|
||
:key="item.value + 'ck'"
|
||
@click.native="handleSetDaySize(item)"
|
||
>
|
||
{{ item.label }}
|
||
</el-dropdown-item>
|
||
</el-dropdown-menu>
|
||
</el-dropdown>
|
||
</div>
|
||
<transition name="el-zoom-in-center">
|
||
<div
|
||
v-show="isShowMsg"
|
||
:style="{
|
||
left: currentProjectMsg.left + 'px',
|
||
top: currentProjectMsg.top + 'px'
|
||
}"
|
||
class="projectMsg"
|
||
>
|
||
<div class="lineMsg projectName">
|
||
{{ currentProjectMsg.name }}
|
||
</div>
|
||
<div class="lineMsg">
|
||
<span class="title">工作周期:</span>
|
||
<span>{{ currentProjectMsg.allTime }}天</span>
|
||
</div>
|
||
<div class="lineMsg">
|
||
<span class="title">当前进度:</span>
|
||
<span>{{ currentProjectMsg.per }}</span>
|
||
</div>
|
||
<div class="lineMsg">
|
||
<span class="title">合格数量:</span>
|
||
<span>{{ currentProjectMsg.per1 }}</span>
|
||
</div>
|
||
<div class="lineMsg">
|
||
<span class="title">开始时间:</span>
|
||
<span>{{ currentProjectMsg.startTime }}</span>
|
||
</div>
|
||
<div class="lineMsg">
|
||
<span class="title">结束时间:</span>
|
||
<span>{{ currentProjectMsg.endTime }}</span>
|
||
</div>
|
||
</div>
|
||
</transition>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import slider from "./components/slider";
|
||
import leftMenu from "./components/leftMenu";
|
||
import {getPlanGantt} from "@/api/srm";
|
||
|
||
export default {
|
||
components: {
|
||
slider,
|
||
leftMenu,
|
||
},
|
||
data() {
|
||
return {
|
||
//当前项是否是子集
|
||
disable: true,
|
||
isChildren: false,
|
||
dialogVal: false,
|
||
//title
|
||
title: "新建",
|
||
//定时器
|
||
timer: null,
|
||
//leftMenu的右侧伸缩线
|
||
rightLineX: 400,
|
||
fixdTopMonth: "",
|
||
//近三年的所有年月日
|
||
allDays: [],
|
||
//所有的天数
|
||
days: [],
|
||
//月数
|
||
monthArr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||
//项目数据
|
||
list: [{
|
||
name: "1-1",
|
||
ower: "",
|
||
per: 8,
|
||
per1: 6,
|
||
type: "1",
|
||
planTime: [1589472000000, 1589817600000],
|
||
stoneTime: "",
|
||
startTime: 1589472000000,
|
||
endTime: 1589817600000,
|
||
left: 20000,
|
||
widthChild: 200,
|
||
widthMe: 200,
|
||
top: 55,
|
||
id: 1589512287571,
|
||
isShow: true,
|
||
parentId: 1589512272596
|
||
}
|
||
],
|
||
//当前hover的项目起止时间
|
||
currentLineDay: {
|
||
start: 0,
|
||
end: 0
|
||
},
|
||
//当前的时间大小
|
||
currentDaySize: {
|
||
label: "天",
|
||
value: 20
|
||
},
|
||
currentDaySizeOptions: [
|
||
{
|
||
label: "天",
|
||
value: 20
|
||
},
|
||
{
|
||
label: "周",
|
||
value: 10
|
||
},
|
||
{
|
||
label: "月",
|
||
value: 2
|
||
}
|
||
],
|
||
//是否显示信息
|
||
isShowMsg: false,
|
||
//当前hover的项目信息
|
||
currentProjectMsg: {
|
||
name: "",
|
||
allTime: 0,
|
||
per: 0,
|
||
per1: 0,
|
||
startTime: 0,
|
||
endTime: 0,
|
||
left: 0,
|
||
top: 0
|
||
},
|
||
isHover: false,
|
||
showFixdTopMonth: false,
|
||
currentListIndex: "",
|
||
//正在编辑的row
|
||
editRow: [],
|
||
currentRow: null,
|
||
// BG滚动高度
|
||
BGScrollTop: 0,
|
||
// 背景高度
|
||
lineBGHeight: 0,
|
||
expandArr: []
|
||
};
|
||
},
|
||
created() {
|
||
let that = this;
|
||
getPlanGantt({}).then(res=>{
|
||
if(res.code===200){
|
||
debugger;
|
||
let arr =[];
|
||
let list = res.data.results;
|
||
list.forEach(item => {
|
||
if (!item.children || item.children.length < 1) {
|
||
let startTime = new Date(item.startTime).getTime();
|
||
let endTime = new Date(item.endDate).getTime();
|
||
let obj=new Object();
|
||
obj.name=item.number;
|
||
obj.id=item.id;
|
||
obj.top=20;
|
||
obj.startTime=startTime;
|
||
obj.endTime=endTime;
|
||
obj.planTime = [startTime,endTime];
|
||
obj.per=item.count;
|
||
obj.per1=item.count_real;
|
||
obj.type=3;
|
||
obj.isShow= true;
|
||
arr.push(obj);
|
||
} else if (item.children && item.children.length >= 1) {
|
||
let startTime = new Date(item.start_date).getTime();
|
||
let endTime = new Date(item.start_date).getTime();
|
||
debugger;
|
||
let temp =[];
|
||
let parentId = item.id;
|
||
let children = item.children;
|
||
children.forEach(child => {
|
||
let start = new Date(item.start_date).getTime();
|
||
let end = new Date(item.end_date).getTime();
|
||
let objChild = new Object();
|
||
objChild.name = child.number;
|
||
objChild.id = child.id;
|
||
objChild.top=50;
|
||
objChild.parentId=parentId;
|
||
objChild.startTime = start;
|
||
objChild.endTime = end;
|
||
objChild.planTime = [start,end];
|
||
objChild.per = child.count;
|
||
objChild.per1 = child.count_real;
|
||
objChild.type = 1;
|
||
objChild.isShow= true;
|
||
|
||
temp.push(objChild);
|
||
});
|
||
let obj=new Object();
|
||
obj.name=item.number;
|
||
obj.id=item.id;
|
||
obj.top=20;
|
||
obj.startTime=startTime;
|
||
obj.endTime=endTime;
|
||
obj.planTime = [startTime,endTime];
|
||
obj.per=item.count;
|
||
obj.per1=item.count_real;
|
||
obj.type=3;
|
||
obj.children = temp;
|
||
obj.isShow= true;
|
||
arr.push(obj);
|
||
}
|
||
that.list = arr;
|
||
that.handlerCheckList(arr);
|
||
});
|
||
console.log(that.list);
|
||
}else{
|
||
that.$message.error(res.msg);
|
||
}
|
||
})
|
||
},
|
||
computed: {
|
||
computedList() {
|
||
debugger;
|
||
console.log(this.list);
|
||
let arr = [];
|
||
this.list.forEach(item => {
|
||
if (!item.children || item.children.length < 1) {
|
||
arr.push(item);
|
||
} else if (item.children && item.children.length >= 1) {
|
||
arr.push(item);
|
||
arr = arr.concat(item.children);
|
||
}
|
||
});
|
||
return arr;
|
||
},
|
||
left() {
|
||
return window.innerWidth - 30;
|
||
},
|
||
//当前年份
|
||
currentYear() {
|
||
return new Date().getFullYear();
|
||
},
|
||
//当前月份
|
||
currentMonth() {
|
||
return new Date().getMonth() + 1;
|
||
},
|
||
//当前日
|
||
currentDay() {
|
||
return new Date().getDate();
|
||
}
|
||
},
|
||
methods: {
|
||
TableScrollTop(val) {
|
||
let lineBG = this.$refs.lineBG;
|
||
lineBG.scrollTo(0, val);
|
||
// lineBG.scrollTo({
|
||
// top: val,
|
||
// left: 0,
|
||
// behavior: "smooth"
|
||
// });
|
||
},
|
||
handlerBGScroll(e) {
|
||
this.BGScrollTop = this.$refs.chart.scrollTop;
|
||
},
|
||
//过滤导入的数据
|
||
handlerCheckList(list) {
|
||
list.forEach((item, index) => {
|
||
item.planTime = [];
|
||
item.left = this.computedTimeWidth(item.startTime);
|
||
item.widthMe = item.widthChild = this.computedTimeWidth(item.startTime, item.endTime) ;
|
||
item.isShow = true;
|
||
if (index - 1 < 0) {
|
||
item.top = 15;
|
||
if (item.type == 3) {
|
||
item.isexpand = true;
|
||
if (item.children.length > 0) {
|
||
item.children.forEach((k, i) => {
|
||
k.planTime = [];
|
||
k.top = item.top + i * 40 + 35;
|
||
k.isShow = true;
|
||
k.left = this.computedTimeWidth(k.startTime);
|
||
k.widthMe = k.widthChild = this.computedTimeWidth(
|
||
k.startTime,
|
||
k.endTime
|
||
);
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
if (list[index - 1].type == 3) {
|
||
item.top =
|
||
list[index - 1].top + list[index - 1].children.length * 40 + 40;
|
||
if (item.type == 3) {
|
||
item.isexpand = true;
|
||
if (item.children.length > 0) {
|
||
item.children.forEach((z, o) => {
|
||
z.planTime = [];
|
||
z.top = item.top + o * 40+40;
|
||
z.isShow = true;
|
||
z.left = this.computedTimeWidth(z.startTime);
|
||
z.widthMe = z.widthChild = this.computedTimeWidth(
|
||
z.startTime,
|
||
z.endTime
|
||
);
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
item.top = list[index - 1].top + 40;
|
||
}
|
||
}
|
||
});
|
||
list.forEach(item => {
|
||
if (item.type == 3) {
|
||
this.setGroupWidth(item.id, list);
|
||
this.setGroupPer(item.id, list);
|
||
}
|
||
});
|
||
this.list = list;
|
||
this.setStoneLine(list);
|
||
window.scrollTo({
|
||
top: 0,
|
||
left: 400,
|
||
behavior: "smooth"
|
||
});
|
||
},
|
||
//设置左侧leftmenu高亮
|
||
handlerSelect(row) {
|
||
this.$refs.leftMenu.handlerSelect(row);
|
||
},
|
||
//分组是否展开
|
||
handlerExpand(row, expand) {
|
||
let rowIndex = this.list.findIndex(item => {
|
||
return item.id == row.id;
|
||
});
|
||
this.list[rowIndex].expand = expand;
|
||
if (
|
||
this.list[rowIndex].children &&
|
||
this.list[rowIndex].children.length > 0
|
||
) {
|
||
this.list[rowIndex].children.forEach(k => {
|
||
k.isShow = expand;
|
||
});
|
||
}
|
||
this.resetTop(rowIndex, !expand, true);
|
||
},
|
||
// 根据时间计算距离
|
||
computedTimeWidth(startTime, endTime) {
|
||
let start = new Date(startTime).getTime();
|
||
let end = new Date(endTime).getTime();
|
||
let left =
|
||
(Math.floor(
|
||
start - new Date(`${this.currentYear - 1}/01/01`).getTime()
|
||
) /
|
||
(1000 * 60 * 60 * 24)) *
|
||
this.currentDaySize.value;
|
||
let width =
|
||
(Math.floor(end - start) / (1000 * 60 * 60 * 24)) *
|
||
this.currentDaySize.value +
|
||
this.currentDaySize.value;
|
||
if (!endTime) {
|
||
return left;
|
||
} else {
|
||
return width;
|
||
}
|
||
},
|
||
//修改后续高度
|
||
resetTop(zindex, reduce, isexpand) {
|
||
// console.log(zindex, reduce, isexpand);
|
||
let num = reduce ? -40 : 40;
|
||
if (!reduce && !isexpand) {
|
||
// console.log(2);
|
||
this.list.forEach((item, index) => {
|
||
if (index > zindex) {
|
||
item.top = item.top + 40;
|
||
if (item.children && item.children.length > 0) {
|
||
item.children.forEach((k, i) => {
|
||
k.top = item.top + 40 * i + 40;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
// console.log(1);
|
||
this.list.forEach((item, index) => {
|
||
if (index > zindex) {
|
||
item.top = item.top + num * this.list[zindex].children.length;
|
||
if (item.children && item.children.length > 0) {
|
||
item.children.forEach((k, i) => {
|
||
k.top = item.top + 40 * i + 40;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
},
|
||
retDelTop(zindex, length) {
|
||
this.list.forEach((item, index) => {
|
||
if (index >= zindex) {
|
||
item.top = item.top - 40 * length;
|
||
if (item.children && item.children.length > 0) {
|
||
item.children.forEach((k, i) => {
|
||
k.top = item.top + 40 * i + 40;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
//根据id设置group的宽度
|
||
setGroupWidth(id, lists) {
|
||
let parent;
|
||
if (lists) {
|
||
parent = lists.find(item => {
|
||
return item.id == id;
|
||
});
|
||
} else {
|
||
parent = this.list.find(item => {
|
||
return item.id == id;
|
||
});
|
||
}
|
||
let left = Math.min.apply(
|
||
Math,
|
||
parent.children.map(o => {
|
||
return o.left;
|
||
})
|
||
);
|
||
let arr = [];
|
||
parent.children.forEach(item => {
|
||
arr.push(item.left + item.widthMe);
|
||
});
|
||
let width = Math.max.apply(Math, arr);
|
||
let widthMe = width - left;
|
||
parent.widthMe = parent.widthChild = widthMe;
|
||
parent.left = left;
|
||
// return parent;
|
||
},
|
||
// 转为分组
|
||
handlerGroup(row) {
|
||
let index = this.list.findIndex(item => {
|
||
return item.id == row.id;
|
||
});
|
||
this.list[index].type = "3";
|
||
this.list[index].per = 0;
|
||
this.list[index].id = new Date().getTime();
|
||
this.list[index].left = 0;
|
||
this.list[index].widthMe = this.list[index].widthChild = 0;
|
||
this.$set(this.list, index, this.list[index]);
|
||
},
|
||
//leftMenu宽度设置
|
||
rightLineMousedown(e) {
|
||
let cx = e.clientX;
|
||
let result;
|
||
document.onmousemove = event => {
|
||
result = event.clientX - cx;
|
||
if (cx + result <= 441) return;
|
||
this.$refs.rightLine.style.left = cx + result + "px";
|
||
};
|
||
document.onmouseup = () => {
|
||
document.onmousemove = document.onmouseup = null;
|
||
if (cx + result <= 441) {
|
||
this.rightLineX = 441;
|
||
return;
|
||
}
|
||
this.rightLineX = cx + result;
|
||
};
|
||
},
|
||
//rowClick事件
|
||
handlerRowClick(row) {
|
||
this.currentRow = row;
|
||
this.$refs.chart.scrollTo({
|
||
top: 0,
|
||
left: row.left - 100,
|
||
behavior: "smooth"
|
||
});
|
||
},
|
||
//更改daySize
|
||
handleSetDaySize(item) {
|
||
// console.log(item);
|
||
this.currentDaySize = item;
|
||
this.days.forEach((item, index) => {
|
||
item.width = (index + 1) * this.currentDaySize.value;
|
||
});
|
||
this.handleScroll();
|
||
if (this.currentRow) {
|
||
let rs = this.computedList.find(o => {
|
||
return o.id == this.currentRow.id;
|
||
});
|
||
this.$nextTick(() => {
|
||
});
|
||
}
|
||
},
|
||
//滑动进度条事件
|
||
thunkMousemove() {
|
||
this.isShowMsg = false;
|
||
},
|
||
//滑动进度条事件
|
||
thunkMousedown() {
|
||
this.isShowMsg = false;
|
||
},
|
||
//滑动进度条事件
|
||
thunkMouseup(per, e, id, parentId) {
|
||
// console.log(per, id, parentId);
|
||
let cindex = this.computedList.findIndex(z => {
|
||
return z.id == id;
|
||
});
|
||
if (parentId) {
|
||
let index = this.list.findIndex(k => {
|
||
return k.id == parentId;
|
||
});
|
||
// console.log(parentId);
|
||
this.list.forEach(item => {
|
||
if (item.id == parentId) {
|
||
// console.log(item, this.list);
|
||
item.children.forEach(k => {
|
||
if (k.id == id) {
|
||
k.per = per;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
this.setGroupPer(parentId);
|
||
this.$set(this.list, index, this.list[index]);
|
||
} else {
|
||
this.list.forEach(item => {
|
||
if (item.id == id) {
|
||
item.per = per;
|
||
}
|
||
});
|
||
}
|
||
this.checkIsin(`line${id}`, e, id, parentId, cindex);
|
||
},
|
||
//设置分组百分比
|
||
setGroupPer(id, lists) {
|
||
let z;
|
||
if (lists) {
|
||
z = lists.find(o => {
|
||
return o.id == id;
|
||
});
|
||
} else {
|
||
z = this.list.find(o => {
|
||
return o.id == id;
|
||
});
|
||
}
|
||
let count = 0;
|
||
let length = z.children.length;
|
||
z.children.forEach(item => {
|
||
count = count + (item.per / 100) * (1 / length);
|
||
});
|
||
z.per = Math.round(count * 100);
|
||
},
|
||
//回到今天
|
||
handleGoToday() {
|
||
let s =
|
||
Math.round(window.innerWidth / this.currentDaySize.value / 2) *
|
||
this.currentDaySize.value - this.rightLineX;
|
||
let width =
|
||
(Math.floor(
|
||
new Date(
|
||
`${this.currentYear}/${this.currentMonth}/${this.currentDay}`
|
||
).getTime() - new Date(`${this.currentYear - 1}/01/01`).getTime()
|
||
) /
|
||
(1000 * 60 * 60 * 24)) *
|
||
this.currentDaySize.value - s;
|
||
window.scrollTo({
|
||
top: 0,
|
||
left: width,
|
||
behavior: "smooth"
|
||
});
|
||
},
|
||
//根据距离计算时间
|
||
/**
|
||
* @param {Number} width
|
||
* @param {Boolean|String} time
|
||
*/
|
||
computedWithTime(width, time) {
|
||
let startTime =
|
||
(width / this.currentDaySize.value) * (1000 * 60 * 60 * 24) +
|
||
new Date(`${this.currentYear - 1}/01/01`).getTime();
|
||
let s = new Date(startTime);
|
||
if (time && time == true) {
|
||
return s.getTime();
|
||
} else if (time && time == "YD") {
|
||
return `${s.getFullYear()}/${s.getMonth() + 1}`;
|
||
} else {
|
||
return `${s.getFullYear()}/${s.getMonth() + 1}/${s.getDate()}`;
|
||
}
|
||
},
|
||
//根据index值和e判断是否在当前line的范围里,是否展示时间和msg框
|
||
/**
|
||
* @param {String} dom ref
|
||
* @param {Object} events $event
|
||
* @param {Number} id id
|
||
* @param {Number} parentId parentId
|
||
* @param {Number} index index
|
||
*/
|
||
checkIsin(dom, events, id, parentId, index) {
|
||
let line = this.$refs[dom][0];
|
||
let lineTop = parseInt(line.style.top);
|
||
let lineDown = lineTop + 16;
|
||
let lineLeft = parseInt(line.style.left);
|
||
let lineRight = parseInt(this.computedList[index].widthMe) + lineLeft;
|
||
if (
|
||
events.pageX - this.rightLineX >= lineLeft &&
|
||
events.pageX - this.rightLineX <= lineRight &&
|
||
events.y - 40 >= lineTop &&
|
||
events.y - 40 <= lineDown
|
||
) {
|
||
this.lineMouseover(dom, events, id, parentId, index);
|
||
this.lineMouseenter(dom, events, id, parentId, index);
|
||
} else {
|
||
this.isShowMsg = false;
|
||
this.currentLineDay = {
|
||
start: 0,
|
||
end: 0
|
||
};
|
||
}
|
||
},
|
||
/**
|
||
* @param {String} dom ref
|
||
* @param {Object} events $event
|
||
* @param {Number} id id
|
||
* @param {Number} parentId parentId
|
||
* @param {Number} index index
|
||
*/
|
||
//鼠标悬停展示上部日期
|
||
lineMouseover(dom, e, id, parentId, index) {
|
||
let start =
|
||
Math.round(
|
||
parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value
|
||
) *
|
||
this.currentDaySize.value +
|
||
this.currentDaySize.value;
|
||
let end =
|
||
parseInt(this.$refs[dom][0].style.left) +
|
||
parseInt(this.$refs[dom][0].style.width);
|
||
end =
|
||
Math.round(end / this.currentDaySize.value) * this.currentDaySize.value;
|
||
this.currentLineDay = {
|
||
start,
|
||
end
|
||
};
|
||
this.isHover = true;
|
||
this.handlerSelect(this.computedList[index]);
|
||
this.lineMouseenter(dom, e, id, parentId, index);
|
||
},
|
||
/**
|
||
* @param {String} dom ref
|
||
* @param {Object} events $event
|
||
* @param {Number} id id
|
||
* @param {Number} parentId parentId
|
||
* @param {Number} index index
|
||
*/
|
||
//鼠标进入显示当前项目的基本信息框
|
||
lineMouseenter(dom, e, id, parentId, index) {
|
||
let start =
|
||
Math.round(
|
||
parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value
|
||
) * this.currentDaySize.value;
|
||
let end =
|
||
parseInt(this.$refs[dom][0].style.left) +
|
||
parseInt(this.$refs[dom][0].style.width);
|
||
end =
|
||
Math.round(end / this.currentDaySize.value) *
|
||
this.currentDaySize.value -
|
||
this.currentDaySize.value;
|
||
this.currentProjectMsg = {
|
||
name: this.computedList[index].name,
|
||
allTime: (end - start) / this.currentDaySize.value + 1,
|
||
per: this.computedList[index].per,
|
||
per1: this.computedList[index].per1,
|
||
startTime: this.computedWithTime(start),
|
||
endTime: this.computedWithTime(end),
|
||
left: e.clientX ,
|
||
top: e.clientY + 20
|
||
};
|
||
/*
|
||
* left:
|
||
e.clientX + 220 - this.$refs.chart.scrollLeft >= this.$refs.chart.clientWidth
|
||
? e.clientX - 220 - 400
|
||
: e.clientX ,
|
||
* */
|
||
this.isShowMsg = true;
|
||
},
|
||
//鼠标离开信息消失,时间显示消失
|
||
/**
|
||
* @param {Object} e $event
|
||
* @param {Boolean} move 是否移动状态
|
||
*/
|
||
lineMouseleave(e, move) {
|
||
if (move) {
|
||
this.isShowMsg = false;
|
||
this.currentProjectMsg = {
|
||
name: "",
|
||
allTime: 0,
|
||
per: 0,
|
||
startTime: 0,
|
||
endTime: 0,
|
||
left: 0,
|
||
top: 0
|
||
};
|
||
// this.handlerSelect();
|
||
return;
|
||
}
|
||
this.currentLineDay = {
|
||
start: 0,
|
||
end: 0
|
||
};
|
||
this.isHover = false;
|
||
this.isShowMsg = false;
|
||
this.currentProjectMsg = {
|
||
name: "",
|
||
allTime: 0,
|
||
per: 0,
|
||
startTime: 0,
|
||
endTime: 0,
|
||
left: 0,
|
||
top: 0
|
||
};
|
||
this.handlerSelect();
|
||
},
|
||
//获取近三年的所有天数
|
||
getDay() {
|
||
this.getAllDate();
|
||
},
|
||
//判断是否为闰年
|
||
/**
|
||
* @param {Number} year 传入年份
|
||
*/
|
||
isLeapYear(year) {
|
||
if (year % 4 > 0) {
|
||
return true;
|
||
}
|
||
return false;
|
||
},
|
||
getAllDate() {
|
||
let obj = {};
|
||
let arr = [this.currentYear - 1, this.currentYear, this.currentYear + 1];
|
||
arr.forEach(item => {
|
||
obj.year = item;
|
||
obj.days = this.isLeapYear(item) ? 365 : 366;
|
||
this.allDays.push(obj);
|
||
obj = {};
|
||
});
|
||
this.checkDate();
|
||
},
|
||
//根据年份天数创建月份及月份天数数组
|
||
checkDate() {
|
||
this.allDays.forEach(item => {
|
||
item.month = this.handleMonthDay(item.days, item.year);
|
||
});
|
||
// console.log(this.allDays);
|
||
this.allDays.forEach(element => {
|
||
if (element.year == this.currentYear) {
|
||
element.month[0][this.currentMonth].forEach(k => {
|
||
if (k.date == this.currentDay) {
|
||
k.today = true;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
let arr = [];
|
||
this.allDays.forEach(item => {
|
||
arr = arr.concat(item.month);
|
||
});
|
||
arr.forEach(item => {
|
||
for (var j in item) {
|
||
this.days = this.days.concat(item[j]);
|
||
}
|
||
});
|
||
this.days.forEach((item, index) => {
|
||
item.width = (index + 1) * this.currentDaySize.value;
|
||
});
|
||
},
|
||
/**
|
||
* @param {Number} days
|
||
* @param {Number} year
|
||
*/
|
||
handleMonthDay(days, year) {
|
||
let arr = [];
|
||
let obj = {};
|
||
this.monthArr.forEach(item => {
|
||
obj[item] = this.checkMonthDays(item, days);
|
||
});
|
||
arr.push(obj);
|
||
let s = arr[0];
|
||
for (let item in s) {
|
||
s[item] = this.addNum(s[item], item, year);
|
||
}
|
||
// console.log(arr);
|
||
return arr;
|
||
},
|
||
/**
|
||
* @param {number} num 天数
|
||
* @param {number} month 月份
|
||
* @param {number} year 年份
|
||
*/
|
||
addNum(num, month, year) {
|
||
let arr = [];
|
||
let obj = {};
|
||
for (let i = 1; i <= num; i++) {
|
||
obj.date = i;
|
||
obj.weekday = new Date(`${year}/ ${month} / ${i}`).getDay();
|
||
arr.push(obj);
|
||
obj = {};
|
||
}
|
||
return arr;
|
||
},
|
||
//根据月份返回天数
|
||
/**
|
||
* @param {Number} month 月份
|
||
* @param {Number} days 天数
|
||
*/
|
||
checkMonthDays(month, days) {
|
||
let day;
|
||
switch (month) {
|
||
case 1:
|
||
case 3:
|
||
case 5:
|
||
case 7:
|
||
case 8:
|
||
case 10:
|
||
case 12:
|
||
return 31;
|
||
// eslint-disable-next-line no-unreachable
|
||
break;
|
||
case 4:
|
||
case 6:
|
||
case 9:
|
||
case 11:
|
||
return 30;
|
||
// eslint-disable-next-line no-unreachable
|
||
break;
|
||
case 2:
|
||
day = days == 365 ? 28 : 29;
|
||
return day;
|
||
// eslint-disable-next-line no-unreachable
|
||
break;
|
||
}
|
||
},
|
||
setList() {
|
||
/*getPlanGantt().then(res=>{
|
||
if(res.code===200){
|
||
this.list = res.data.results;
|
||
}else{
|
||
this.$message.error(res.msg);
|
||
}
|
||
})*/
|
||
},
|
||
//设置里程碑线的高度
|
||
setStoneLine(isFirst) {
|
||
this.$nextTick(() => {
|
||
let height = window.getComputedStyle(
|
||
document.getElementsByClassName("lineBG")[0]
|
||
).height;
|
||
this.lineBGHeight = height;
|
||
});
|
||
},
|
||
//顶部固定时间
|
||
handleScroll() {
|
||
let w = window.scrollX;
|
||
if (w <= 62) {
|
||
this.showFixdTopMonth = false;
|
||
} else {
|
||
this.showFixdTopMonth = true;
|
||
}
|
||
let time = this.computedWithTime(w, "YD");
|
||
this.fixdTopMonth = time;
|
||
}
|
||
},
|
||
watch: {
|
||
currentDaySize(newValue, oldValue) {
|
||
this.list.forEach(item => {
|
||
item.left = (item.left / oldValue.value) * newValue.value;
|
||
item.widthMe = item.widthChild =
|
||
(item.widthMe / oldValue.value) * newValue.value;
|
||
if (item.children && item.children.length > 0) {
|
||
item.children.forEach(k => {
|
||
k.left = (k.left / oldValue.value) * newValue.value;
|
||
k.widthMe = k.widthChild =
|
||
(k.widthMe / oldValue.value) * newValue.value;
|
||
});
|
||
}
|
||
});
|
||
}
|
||
},
|
||
mounted() {
|
||
document.addEventListener("scroll", this.handleScroll);
|
||
this.getDay();
|
||
this.setList();
|
||
this.setStoneLine();
|
||
},
|
||
beforeDestroy() {
|
||
window.removeEventListener("scroll", this.handleScroll);
|
||
document.onmousemove = document.onmouseup = null;
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.chart {
|
||
height: 90vh;
|
||
user-select: none;
|
||
position: relative;
|
||
overflow: hidden;
|
||
overflow-x: scroll;
|
||
background: #ffffff;
|
||
|
||
.header {
|
||
height: 40px;
|
||
position: relative;
|
||
|
||
.header-left {
|
||
margin-left: 10px;
|
||
box-sizing: border-box;
|
||
padding-top: 5px;
|
||
height: 40px;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
background-color: #fff;
|
||
}
|
||
}
|
||
|
||
.left {
|
||
position: fixed;
|
||
/*height: calc(100% - 40px);*/
|
||
height: 89vh;
|
||
background-color: #fff;
|
||
z-index: 999;
|
||
// border-right: 1px solid #d7d7d7;
|
||
.rightLine {
|
||
z-index: 999;
|
||
position: absolute;
|
||
top: 0;
|
||
height: 100%;
|
||
width: 2px;
|
||
background-color: #d7d7d7;
|
||
box-shadow: 4px 2px 12px 0 rgba(0, 0, 0, 0.2);
|
||
cursor: col-resize;
|
||
|
||
&:hover {
|
||
background-color: #409eff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.showCurrentLineDate {
|
||
position: fixed;
|
||
top: 0;
|
||
background-color: #409eff;
|
||
color: #fff;
|
||
height: 20px;
|
||
font-size: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.projectMsg {
|
||
box-sizing: border-box;
|
||
padding: 20px;
|
||
position: fixed;
|
||
width: 230px;
|
||
/*height: 230px;*/
|
||
z-index: 12000;
|
||
background-color: #fff;
|
||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||
border-radius: 4px;
|
||
border: 1px solid #ebeef5;
|
||
|
||
.lineMsg {
|
||
margin-bottom: 10px;
|
||
|
||
&.projectName {
|
||
font-size: 22px;
|
||
color: #333333;
|
||
text-align: center;
|
||
border-bottom: 1px solid #999999;
|
||
}
|
||
span {
|
||
font-size: 14px;
|
||
color: #909090;
|
||
}
|
||
|
||
.title {
|
||
margin-right: 5px;
|
||
color: #666666
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
.date {
|
||
display: flex;
|
||
height: 100%;
|
||
position: relative;
|
||
/*overflow: scroll;*/
|
||
.topMonth {
|
||
// width: 100px;
|
||
background-color: #fff;
|
||
position: fixed;
|
||
top: 40px;
|
||
height: 21px;
|
||
line-height: 21px;
|
||
font-size: 12px !important;
|
||
font-weight: 600 !important;
|
||
color: #909090 !important;
|
||
}
|
||
|
||
.toolTip {
|
||
position: fixed;
|
||
right: 0;
|
||
top: 90px;
|
||
z-index: 999;
|
||
|
||
.base {
|
||
display: inline-block;
|
||
text-align: center;
|
||
height: 30px;
|
||
line-height: 30px;
|
||
padding: 0 15px;
|
||
background-color: #fff;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||
font-weight: 400;
|
||
color: #303030;
|
||
border-radius: 3px;
|
||
box-shadow: 0 3px 12px 0 rgba(48, 48, 48, 0.05),
|
||
0 3px 6px 0 rgba(48, 48, 48, 0.1);
|
||
margin-right: 20px;
|
||
|
||
&:hover {
|
||
color: #409eff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.group {
|
||
position: absolute;
|
||
background-color: #909090 !important;
|
||
border: none !important;
|
||
border-radius: 0 !important;
|
||
height: 14px !important;
|
||
line-height: 14px !important;
|
||
// margin-top: 5px;
|
||
clip-path: polygon(
|
||
100% 0,
|
||
100% 100%,
|
||
calc(100% - 8px) 60%,
|
||
8px 60%,
|
||
0 100%,
|
||
0 0
|
||
);
|
||
// > div {
|
||
// -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||
// }
|
||
.progress {
|
||
// width: 50px;
|
||
background-color: #606060 !important;
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.line {
|
||
position: absolute;
|
||
height: 20px;
|
||
background: #abf7f7;
|
||
.rightCurDrag {
|
||
cursor: e-resize;
|
||
width: 10px;
|
||
background-color: #000;
|
||
height: 16px;
|
||
position: absolute;
|
||
right: -15px;
|
||
transform: translateY(-50%);
|
||
top: 50%;
|
||
border-radius: 3px;
|
||
user-select: none;
|
||
}
|
||
|
||
.leftCurDrag {
|
||
cursor: e-resize;
|
||
width: 10px;
|
||
background-color: #000;
|
||
height: 16px !important;
|
||
position: absolute;
|
||
left: -15px;
|
||
transform: translateY(-50%);
|
||
top: 50%;
|
||
border-radius: 3px;
|
||
user-select: none;
|
||
}
|
||
|
||
.stoneLine {
|
||
position: absolute;
|
||
top: 0px;
|
||
left: 50%;
|
||
margin-left: -1px;
|
||
width: 2px;
|
||
background-color: #24b47e;
|
||
}
|
||
|
||
.milestone {
|
||
position: absolute;
|
||
left: 50%;
|
||
// transform: translateX(-50%);
|
||
cursor: move;
|
||
margin-left: -10px;
|
||
width: 20px;
|
||
height: 20px;
|
||
background-color: #24b47e;
|
||
transform: rotate(-45deg);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
i {
|
||
transform: rotate(45deg);
|
||
color: #fff;
|
||
z-index: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
.years {
|
||
display: flex;
|
||
height: 100%;
|
||
|
||
.month {
|
||
height: 100%;
|
||
|
||
.month-top {
|
||
height: 21px;
|
||
line-height: 21px;
|
||
font-size: 12px !important;
|
||
font-weight: 600 !important;
|
||
color: #909090 !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.allDaysArray {
|
||
position: absolute;
|
||
left: 0px;
|
||
top: 21px;
|
||
height: calc(100% - 21px);
|
||
|
||
.alldays {
|
||
display: flex;
|
||
}
|
||
|
||
.lineBG {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
overflow: scroll;
|
||
}
|
||
|
||
.weekday {
|
||
color: #c7c7c7;
|
||
}
|
||
|
||
.weekday1 {
|
||
border-left: 1px solid #d7d7d7;
|
||
background-image: linear-gradient(to bottom, #f8f8f8, #f8f8f8);
|
||
}
|
||
|
||
.weekday2 {
|
||
border-right: 1px solid #d7d7d7;
|
||
background-image: linear-gradient(to bottom, #f8f8f8, #f8f8f8);
|
||
width: 100%;
|
||
}
|
||
|
||
.day {
|
||
// width: 40px;
|
||
position: relative;
|
||
display: inline-block;
|
||
height: calc(100% - 21px);
|
||
box-sizing: border-box;
|
||
text-align: center;
|
||
|
||
.isHover {
|
||
background-color: #409eff;
|
||
color: #fff;
|
||
}
|
||
|
||
.nodBorder {
|
||
div {
|
||
border-left: none !important;
|
||
}
|
||
}
|
||
|
||
.dateNum {
|
||
font-size: 12px;
|
||
line-height: 20px;
|
||
display: block;
|
||
height: 20px;
|
||
// border-left: 1px solid #d7d7d7;
|
||
border-bottom: 1px solid #d7d7d7;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.todayDateNum {
|
||
background-color: #f0a100;
|
||
color: #fff;
|
||
}
|
||
|
||
.dateBG {
|
||
position: absolute;
|
||
top: 20px;
|
||
left: 0px;
|
||
display: block;
|
||
// height: 100%;
|
||
}
|
||
|
||
.today {
|
||
position: relative;
|
||
}
|
||
|
||
.today::after {
|
||
content: "";
|
||
height: 100%;
|
||
width: 2px;
|
||
background-color: #f0a100;
|
||
position: absolute;
|
||
// left: 20px;
|
||
top: 0px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|