mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-13 03:52:50 +00:00
perf: 优化甘特图
This commit is contained in:
parent
b83ce02849
commit
5adbd6e8f1
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="wook-gantt">
|
||||
<div class="common-gantt">
|
||||
<div class="gantt-left" :style="{width:menuWidth+'px'}">
|
||||
<div class="gantt-title">
|
||||
<div class="gantt-title-text">{{$L('任务名称')}}</div>
|
||||
@ -26,7 +26,7 @@
|
||||
<li v-for="(item, key) in dateNum" :key="key" :style="dateStyle(key)">
|
||||
<div class="date-format">
|
||||
<div class="format-day">{{dateFormat(key, 'day')}}</div>
|
||||
<div v-if="dateWidth > 46" class="format-wook">{{dateFormat(key, 'wook')}}</div>
|
||||
<div v-if="dateWidth > 46" class="format-week">{{dateFormat(key, 'week')}}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@ -39,7 +39,7 @@
|
||||
class="timeline-item"
|
||||
:style="itemStyle(item)"
|
||||
@mousedown="itemMouseDown($event, item)">
|
||||
<div class="timeline-title">{{item.label}}</div>
|
||||
<div class="timeline-title" :title="item.label">{{item.label}}</div>
|
||||
<div class="timeline-resizer"></div>
|
||||
</div>
|
||||
</li>
|
||||
@ -184,7 +184,7 @@ export default {
|
||||
let date = new Date(new Date().getTime() + j * 86400000)
|
||||
if (type == 'day') {
|
||||
return date.getDate();
|
||||
} else if (type == 'wook') {
|
||||
} else if (type == 'week') {
|
||||
return this.$L(`星期${'日一二三四五六'.charAt(date.getDay())}`);
|
||||
} else {
|
||||
return date;
|
||||
@ -291,7 +291,6 @@ export default {
|
||||
type: type,
|
||||
clientX: e.clientX,
|
||||
value: item[type],
|
||||
time: item.time,
|
||||
};
|
||||
this.mouseItem = item;
|
||||
this.dateMove = null;
|
||||
@ -299,18 +298,16 @@ export default {
|
||||
itemMouseMove(e) {
|
||||
if (this.mouseItem != null) {
|
||||
e.preventDefault();
|
||||
var diff = e.clientX - this.mouseBak.clientX;
|
||||
const {start, end} = this.mouseBak.time;
|
||||
let date = new Date();
|
||||
let nowTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0).getTime();
|
||||
let diffStartDay = (start - nowTime) / 1000 / 60 / 60 / 24;
|
||||
let diffEndDay = (end - nowTime) / 1000 / 60 / 60 / 24;
|
||||
let width = this.dateWidth * (diffEndDay - diffStartDay);
|
||||
width += this.mouseBak.value + diff;
|
||||
if (width <= 0) {
|
||||
return false;
|
||||
const diff = this.mouseBak.value + (e.clientX - this.mouseBak.clientX);
|
||||
if (this.mouseBak.type === 'moveW') {
|
||||
const oneWidthTime = 86400000 / this.dateWidth;
|
||||
const {start, end} = this.mouseItem.time;
|
||||
let moveTime = diff * oneWidthTime;
|
||||
if (end + moveTime - start <= 0) {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.$set(this.mouseItem, this.mouseBak.type, this.mouseBak.value + diff);
|
||||
this.$set(this.mouseItem, this.mouseBak.type, diff);
|
||||
} else if (this.dateMove != null) {
|
||||
e.preventDefault();
|
||||
let moveX = (this.dateMove.clientX - e.clientX) * 5;
|
||||
@ -340,9 +337,6 @@ export default {
|
||||
this.$set(this.mouseItem, 'moveW', 0);
|
||||
isM = true;
|
||||
}
|
||||
if (this.mouseItem.time && this.mouseItem.time.start > this.mouseItem.time.end) {
|
||||
return false;
|
||||
}
|
||||
//
|
||||
if (isM) {
|
||||
this.$emit("on-change", this.mouseItem)
|
||||
@ -371,3 +365,265 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.common-gantt {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: self-start;
|
||||
color: #747a81;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.gantt-left {
|
||||
flex-grow:0;
|
||||
flex-shrink:0;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.gantt-title {
|
||||
height: 76px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background-color: #F9FAFB;
|
||||
padding-left: 12px;
|
||||
overflow: hidden;
|
||||
.gantt-title-text {
|
||||
line-height: 100px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.gantt-item {
|
||||
transform: translateZ(0);
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
> li {
|
||||
height: 40px;
|
||||
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
&:hover {
|
||||
.item-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.item-overdue {
|
||||
flex-grow:0;
|
||||
flex-shrink:0;
|
||||
color: #ffffff;
|
||||
margin-right: 4px;
|
||||
background-color: #ff0000;
|
||||
padding: 1px 3px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.item-title {
|
||||
flex: 1;
|
||||
padding-right: 12px;
|
||||
cursor: default;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&.complete {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
&.overdue {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.item-icon {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
margin-right: 2px;
|
||||
font-size: 16px;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-right {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.gantt-chart {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: translateZ(0);
|
||||
.gantt-month {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
height: 26px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
background-color: #F9FAFB;
|
||||
> li {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.month-format {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding: 6px 6px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
cursor: move;
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
> li {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.date-format {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 44px;
|
||||
.format-day {
|
||||
line-height: 28px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.format-week {
|
||||
line-height: 16px;
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-timeline {
|
||||
position: absolute;
|
||||
top: 76px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
> li {
|
||||
cursor: default;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
|
||||
position: relative;
|
||||
.timeline-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
touch-action: none;
|
||||
pointer-events: auto;
|
||||
padding: 4px;
|
||||
margin-top: 4px;
|
||||
background: #e74c3c;
|
||||
border-radius: 18px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
will-change: contents;
|
||||
height: 32px;
|
||||
.timeline-title {
|
||||
touch-action: none;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-left: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.timeline-resizer {
|
||||
height: 22px;
|
||||
touch-action: none;
|
||||
width: 8px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
cursor: ew-resize;
|
||||
flex-shrink: 0;
|
||||
will-change: visibility;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
18
resources/assets/js/functions/web.js
vendored
18
resources/assets/js/functions/web.js
vendored
@ -386,24 +386,6 @@
|
||||
getDialogUnread(dialog) {
|
||||
return dialog ? (dialog.unread || dialog.is_mark_unread || 0) : 0
|
||||
},
|
||||
|
||||
/**
|
||||
* 克隆对象
|
||||
* @param myObj
|
||||
* @returns {*}
|
||||
*/
|
||||
cloneData(myObj) {
|
||||
if (typeof (myObj) !== 'object') return myObj;
|
||||
if (myObj === null) return myObj;
|
||||
//
|
||||
if (typeof myObj.length === 'number') {
|
||||
let [...myNewObj] = myObj;
|
||||
return myNewObj;
|
||||
} else {
|
||||
let {...myNewObj} = myObj;
|
||||
return myNewObj;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -1,20 +1,26 @@
|
||||
<template>
|
||||
<div class="project-gstc-gantt">
|
||||
<GanttView :lists="lists" :menuWidth="windowWidth ? 180 : 260" :itemWidth="80" @on-change="updateTime" @on-click="clickItem"/>
|
||||
<Dropdown class="project-gstc-dropdown-filtr" :style="windowWidth?{left:'142px'}:{}" @on-click="tapProject">
|
||||
<Icon class="project-gstc-dropdown-icon" :class="{filtr:filtrProjectId>0}" type="md-funnel" />
|
||||
<GanttView
|
||||
:lists="lists"
|
||||
:menuWidth="menuWidth"
|
||||
:itemWidth="80"
|
||||
@on-change="onChange"
|
||||
@on-click="onClick"/>
|
||||
<Dropdown class="project-gstc-dropdown-filtr" :style="dropStyle" trigger="click" @on-click="onSwitchColumn">
|
||||
<Icon class="project-gstc-dropdown-icon" :class="{filtr:filtrProjectId > 0}" type="md-funnel" />
|
||||
<DropdownMenu slot="list">
|
||||
<DropdownItem :name="0" :class="{'dropdown-active':filtrProjectId==0}">{{$L('全部')}}</DropdownItem>
|
||||
<DropdownItem v-for="(item, index) in projectLabel"
|
||||
:key="index"
|
||||
:name="item.id"
|
||||
:class="{'dropdown-active':filtrProjectId==item.id}">
|
||||
{{item.name}}
|
||||
<span v-if="item.tasks">({{item.tasks.length}})</span>
|
||||
<DropdownItem :name="0" :class="{'dropdown-active':filtrProjectId == 0}">{{ $L('全部') }}</DropdownItem>
|
||||
<DropdownItem
|
||||
v-for="(item, index) in projectColumn"
|
||||
:key="index"
|
||||
:name="item.id"
|
||||
:class="{'dropdown-active':filtrProjectId == item.id}">
|
||||
{{ item.name }}
|
||||
<span v-if="item.tasks">({{ item.tasks.length }})</span>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
<div class="project-gstc-edit" :class="{info:editShowInfo, visible:editData&&editData.length > 0}">
|
||||
<div class="project-gstc-edit" :class="{info:editShowInfo, visible:editData && editData.length > 0}">
|
||||
<div class="project-gstc-edit-info">
|
||||
<Table size="small" max-height="600" :columns="editColumns" :data="editData"></Table>
|
||||
<div class="project-gstc-edit-btns">
|
||||
@ -33,85 +39,83 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GanttView from "./GanttView";
|
||||
import {mapState} from "vuex";
|
||||
import GanttView from "../../../components/GanttView";
|
||||
|
||||
/**
|
||||
* 甘特图
|
||||
*/
|
||||
export default {
|
||||
name: 'ProjectGantt',
|
||||
components: {GanttView },
|
||||
components: {GanttView},
|
||||
props: {
|
||||
projectLabel: {
|
||||
projectColumn: {
|
||||
default: []
|
||||
},
|
||||
lineTaskData: {
|
||||
default: []
|
||||
},
|
||||
levelList: {},
|
||||
},
|
||||
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
loadFinish: false,
|
||||
|
||||
lists: [],
|
||||
|
||||
editColumns: [],
|
||||
editData: [],
|
||||
editShowInfo: false,
|
||||
editLoad: 0,
|
||||
|
||||
filtrProjectId: 0,
|
||||
|
||||
editColumns: [
|
||||
{
|
||||
title: this.$L('任务名称'),
|
||||
key: 'label',
|
||||
minWidth: 150,
|
||||
ellipsis: true,
|
||||
}, {
|
||||
title: this.$L('原计划时间'),
|
||||
minWidth: 135,
|
||||
align: 'center',
|
||||
render: (h, {row}) => {
|
||||
if (row.notime === true) {
|
||||
return h('span', '-');
|
||||
}
|
||||
return h('div', {
|
||||
style: {},
|
||||
}, [
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(row.baktime.start / 1000))),
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(row.baktime.end / 1000)))
|
||||
]);
|
||||
}
|
||||
}, {
|
||||
title: this.$L('新计划时间'),
|
||||
minWidth: 135,
|
||||
align: 'center',
|
||||
render: (h, {row}) => {
|
||||
return h('div', {
|
||||
style: {},
|
||||
}, [
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(row.newTime.start / 1000))),
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(row.newTime.end / 1000)))
|
||||
]);
|
||||
}
|
||||
}
|
||||
],
|
||||
editData: [],
|
||||
editLoad: 0,
|
||||
editShowInfo: false,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editColumns = [
|
||||
{
|
||||
title: this.$L('任务名称'),
|
||||
key: 'label',
|
||||
minWidth: 150,
|
||||
ellipsis: true,
|
||||
}, {
|
||||
title: this.$L('原计划时间'),
|
||||
minWidth: 135,
|
||||
align: 'center',
|
||||
render: (h, params) => {
|
||||
if (params.row.notime === true) {
|
||||
return h('span', '-');
|
||||
}
|
||||
return h('div', {
|
||||
style: {},
|
||||
}, [
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(params.row.backTime.start / 1000))),
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(params.row.backTime.end / 1000)))
|
||||
]);
|
||||
}
|
||||
}, {
|
||||
title: this.$L('新计划时间'),
|
||||
minWidth: 135,
|
||||
align: 'center',
|
||||
render: (h, params) => {
|
||||
return h('div', {
|
||||
style: {},
|
||||
}, [
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(params.row.newTime.start / 1000))),
|
||||
h('div', $A.formatDate('Y-m-d H:i', Math.round(params.row.newTime.end / 1000)))
|
||||
]);
|
||||
}
|
||||
}
|
||||
];
|
||||
//
|
||||
this.initData();
|
||||
this.loadFinish = true;
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['userId', 'windowWidth', 'taskPriority']),
|
||||
|
||||
menuWidth() {
|
||||
return this.windowWidth < 1440 ? 180 : 260;
|
||||
},
|
||||
|
||||
dropStyle() {
|
||||
return this.windowWidth < 1440 ? {left: '142px'} : {};
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
projectLabel: {
|
||||
|
||||
watch: {
|
||||
projectColumn: {
|
||||
handler() {
|
||||
this.initData();
|
||||
},
|
||||
@ -120,13 +124,18 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
verifyLists(item){
|
||||
initData() {
|
||||
this.lists = [];
|
||||
this.projectColumn && this.projectColumn.some(this.checkAdd);
|
||||
},
|
||||
|
||||
checkAdd(item) {
|
||||
if (this.filtrProjectId > 0) {
|
||||
if (item.id != this.filtrProjectId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
item.tasks && item.tasks.forEach((taskData) => {
|
||||
item.tasks && item.tasks.some(taskData => {
|
||||
let notime = !taskData.start_at || !taskData.end_at;
|
||||
let times = this.getTimeObj(taskData);
|
||||
let start = times.start;
|
||||
@ -134,10 +143,10 @@ export default {
|
||||
//
|
||||
let color = '#058ce4';
|
||||
if (taskData.complete_at) {
|
||||
return;
|
||||
return;
|
||||
} else {
|
||||
// 等级颜色
|
||||
this.levelList.some(level => {
|
||||
this.taskPriority.some(level => {
|
||||
if (level.priority === taskData.p_level) {
|
||||
color = level.color;
|
||||
return true;
|
||||
@ -145,11 +154,11 @@ export default {
|
||||
});
|
||||
}
|
||||
//
|
||||
let tempTime = { start, end };
|
||||
let findData = this.editData.find((t) => { return t.id == taskData.id });
|
||||
let tempTime = {start, end};
|
||||
let bakTime = $A.cloneJSON(tempTime)
|
||||
let findData = this.editData.find(({id}) => id == taskData.id);
|
||||
if (findData) {
|
||||
findData.backTime = $A.cloneData(tempTime)
|
||||
tempTime = $A.cloneData(findData.newTime);
|
||||
tempTime = $A.cloneJSON(findData.newTime);
|
||||
}
|
||||
//
|
||||
this.lists.push({
|
||||
@ -159,108 +168,80 @@ export default {
|
||||
overdue: taskData.overdue,
|
||||
time: tempTime,
|
||||
notime: notime,
|
||||
style: { background: color },
|
||||
baktime: bakTime,
|
||||
style: {background: color},
|
||||
});
|
||||
});
|
||||
},
|
||||
initData() {
|
||||
this.lists = [];
|
||||
this.projectLabel && this.projectLabel.forEach((item) => {
|
||||
this.verifyLists(item);
|
||||
});
|
||||
if (this.lists&&this.lists.length == 0) {
|
||||
this.lineTaskData && this.lineTaskData.forEach((item) => {
|
||||
this.verifyLists(item);
|
||||
});
|
||||
}
|
||||
//
|
||||
if (this.lists&&this.lists.length == 0 && this.filtrProjectId == 0) {
|
||||
$A.modalWarning({
|
||||
content: '任务列表为空,请先添加任务。',
|
||||
onOk: () => {
|
||||
this.$emit('on-close');
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
updateTime(item) {
|
||||
let original = this.getRawTime(item.id);
|
||||
if (Math.abs(original.end - item.time.end) > 1000 || Math.abs(original.start - item.time.start) > 1000) {
|
||||
onChange(item) {
|
||||
const {time, baktime} = item;
|
||||
if (Math.abs(baktime.end - time.end) > 1000 || Math.abs(baktime.start - time.start) > 1000) {
|
||||
//修改时间(变化超过1秒钟)
|
||||
let backTime = $A.cloneData(original);
|
||||
let newTime = $A.cloneData(item.time);
|
||||
let findData = this.editData.find(({id}) => id == item.id);
|
||||
if (findData) {
|
||||
findData.newTime = newTime;
|
||||
findData.newTime = time;
|
||||
} else {
|
||||
this.editData.push({
|
||||
id: item.id,
|
||||
label: item.label,
|
||||
notime: item.notime,
|
||||
backTime,
|
||||
newTime,
|
||||
baktime: item.baktime,
|
||||
newTime: time,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clickItem(item) {
|
||||
onClick(item) {
|
||||
this.$store.dispatch("openTask", item);
|
||||
},
|
||||
|
||||
editSubmit(save) {
|
||||
this.editData&&this.editData.forEach((item) => {
|
||||
this.editData && this.editData.forEach(item => {
|
||||
let task = this.lists.find(({id}) => id == item.id)
|
||||
if (save) {
|
||||
this.editLoad++;
|
||||
let timeStart = $A.formatDate('Y-m-d H:i', Math.round(item.newTime.start / 1000));
|
||||
let timeEnd = $A.formatDate('Y-m-d H:i', Math.round(item.newTime.end / 1000));
|
||||
let dataJson = {
|
||||
task_id: item.id,
|
||||
times:[timeStart,timeEnd],
|
||||
times: [timeStart, timeEnd],
|
||||
};
|
||||
this.$store.dispatch("taskUpdate", dataJson).then(({msg}) => {
|
||||
$A.messageSuccess(msg);
|
||||
this.editLoad--;
|
||||
if (typeof successCallback === "function") successCallback();
|
||||
this.editLoad === 0 && $A.messageSuccess(msg);
|
||||
task && this.$set(task, 'baktime', $A.cloneJSON(task.time));
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
this.editLoad--;
|
||||
this.editLoad === 0 && $A.modalError(msg);
|
||||
task && this.$set(task, 'time', $A.cloneJSON(task.baktime));
|
||||
})
|
||||
} else {
|
||||
this.lists.some((task) => {
|
||||
if (task.id == item.id) {
|
||||
this.$set(task, 'time', item.backTime);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
task && this.$set(task, 'time', $A.cloneJSON(task.baktime));
|
||||
}
|
||||
});
|
||||
this.editData = [];
|
||||
},
|
||||
|
||||
getRawTime(taskId) {
|
||||
let times = null;
|
||||
this.lists.some((taskData) => {
|
||||
if (taskData.id == taskId) {
|
||||
times = this.getTimeObj(taskData);
|
||||
}
|
||||
});
|
||||
return times;
|
||||
let task = this.lists.find(({id}) => id == taskId)
|
||||
return task ? this.getTimeObj(task) : null;
|
||||
},
|
||||
|
||||
getTimeObj(taskData) {
|
||||
let start = $A.Time(taskData.start_at) || $A.Time(taskData.created_at);
|
||||
let end = $A.Time(taskData.end_at) || ($A.Time(taskData.created_at) + 86400);
|
||||
if (end == start) {
|
||||
end = Math.round(new Date($A.formatDate('Y-m-d 23:59:59', end)).getTime()/1000);
|
||||
end = Math.round(new Date($A.formatDate('Y-m-d 23:59:59', end)).getTime() / 1000);
|
||||
}
|
||||
end = Math.max(end, start + 60);
|
||||
start*= 1000;
|
||||
end*= 1000;
|
||||
start *= 1000;
|
||||
end *= 1000;
|
||||
return {start, end};
|
||||
},
|
||||
|
||||
tapProject(e) {
|
||||
onSwitchColumn(e) {
|
||||
this.filtrProjectId = $A.runNum(e);
|
||||
this.initData();
|
||||
},
|
||||
|
||||
@ -314,11 +314,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="tabTypeActive === 'gantt'" class="project-gantt">
|
||||
<ProjectGantt
|
||||
:lineData="ganttColumnList[0].tasks"
|
||||
:projectLabel="ganttColumnList"
|
||||
:lineTaskData="ganttColumnList[0].tasks"
|
||||
:levelList="taskPriority"/>
|
||||
<!--甘特图-->
|
||||
<ProjectGantt :projectColumn="columnList"/>
|
||||
</div>
|
||||
<!--项目设置-->
|
||||
<Modal
|
||||
@ -556,8 +553,6 @@ export default {
|
||||
'userId',
|
||||
'cacheDialogs',
|
||||
|
||||
'taskPriority',
|
||||
|
||||
'projectId',
|
||||
'projectLoad',
|
||||
'cacheTasks',
|
||||
@ -685,26 +680,6 @@ export default {
|
||||
return list;
|
||||
},
|
||||
|
||||
ganttColumnList() {
|
||||
const {projectId, cacheColumns} = this;
|
||||
return cacheColumns.filter((row) => {
|
||||
row.tasks = row.tasks.filter(task => {
|
||||
return task.column_id == row.id && !task.complete_at;
|
||||
}).sort((a, b) => {
|
||||
if (a.sort != b.sort) {
|
||||
return a.sort - b.sort;
|
||||
}
|
||||
return a.id - b.id;
|
||||
});
|
||||
return row.project_id == projectId && row.tasks.length > 0;
|
||||
}).sort((a, b) => {
|
||||
if (a.sort != b.sort) {
|
||||
return a.sort - b.sort;
|
||||
}
|
||||
return a.id - b.id;
|
||||
});
|
||||
},
|
||||
|
||||
myList() {
|
||||
const {allTask, taskCompleteTemps, sortField, sortType} = this;
|
||||
let array = allTask.filter(task => this.myFilter(task));
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
@import "file-content";
|
||||
@import "project-archived";
|
||||
@import "project-dialog";
|
||||
@import "project-gantt";
|
||||
@import "project-list";
|
||||
@import "project-log";
|
||||
@import "project-management";
|
||||
@ -14,5 +15,3 @@
|
||||
@import "task-menu";
|
||||
@import "task-priority";
|
||||
@import "team-management";
|
||||
@import "project-gantt";
|
||||
@import "gantt-view";
|
||||
|
||||
@ -1,261 +0,0 @@
|
||||
.wook-gantt {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: self-start;
|
||||
color: #747a81;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.gantt-left {
|
||||
flex-grow:0;
|
||||
flex-shrink:0;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.gantt-title {
|
||||
height: 76px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background-color: #F9FAFB;
|
||||
padding-left: 12px;
|
||||
overflow: hidden;
|
||||
.gantt-title-text {
|
||||
line-height: 100px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.gantt-item {
|
||||
transform: translateZ(0);
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
> li {
|
||||
height: 40px;
|
||||
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
&:hover {
|
||||
.item-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.item-overdue {
|
||||
flex-grow:0;
|
||||
flex-shrink:0;
|
||||
color: #ffffff;
|
||||
margin-right: 4px;
|
||||
background-color: #ff0000;
|
||||
padding: 1px 3px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.item-title {
|
||||
flex: 1;
|
||||
padding-right: 12px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&.complete {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
&.overdue {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.item-icon {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
margin-right: 2px;
|
||||
font-size: 16px;
|
||||
color: #888888;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-right {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.gantt-chart {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: translateZ(0);
|
||||
.gantt-month {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
height: 26px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
background-color: #F9FAFB;
|
||||
> li {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.month-format {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding: 6px 6px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
cursor: move;
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
> li {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: rgba(237, 241, 242, 0.75);
|
||||
}
|
||||
.date-format {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 44px;
|
||||
.format-day {
|
||||
line-height: 28px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.format-wook {
|
||||
line-height: 16px;
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gantt-timeline {
|
||||
position: absolute;
|
||||
top: 76px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
> li {
|
||||
cursor: default;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
|
||||
position: relative;
|
||||
.timeline-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
touch-action: none;
|
||||
pointer-events: auto;
|
||||
padding: 4px;
|
||||
margin-top: 4px;
|
||||
background: #e74c3c;
|
||||
border-radius: 18px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
will-change: contents;
|
||||
height: 32px;
|
||||
.timeline-title {
|
||||
touch-action: none;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-left: 4px;
|
||||
margin-right: 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.timeline-resizer {
|
||||
height: 22px;
|
||||
touch-action: none;
|
||||
width: 8px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
cursor: ew-resize;
|
||||
flex-shrink: 0;
|
||||
will-change: visibility;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user