mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-16 14:12:51 +00:00
feat: 项目任务新增一个甘特图展示选项
This commit is contained in:
parent
3006dc6584
commit
15ecd8d592
20
resources/assets/js/functions/web.js
vendored
20
resources/assets/js/functions/web.js
vendored
@ -385,7 +385,25 @@
|
|||||||
*/
|
*/
|
||||||
getDialogUnread(dialog) {
|
getDialogUnread(dialog) {
|
||||||
return dialog ? (dialog.unread || dialog.is_mark_unread || 0) : 0
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
359
resources/assets/js/pages/manage/components/GanttView.vue
Normal file
359
resources/assets/js/pages/manage/components/GanttView.vue
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wook-gantt">
|
||||||
|
<div class="gantt-left" :style="{width:menuWidth+'px'}">
|
||||||
|
<div class="gantt-title">
|
||||||
|
<div class="gantt-title-text">{{$L('任务名称')}}</div>
|
||||||
|
</div>
|
||||||
|
<ul ref="ganttItem"
|
||||||
|
class="gantt-item"
|
||||||
|
@scroll="itemScrollListener"
|
||||||
|
@mouseenter="mouseType='item'">
|
||||||
|
<li v-for="(item, key) in lists" :key="key">
|
||||||
|
<div v-if="item.overdue" class="item-overdue" @click="clickItem(item)">{{$L('已超期')}}</div>
|
||||||
|
<div class="item-title" :class="{complete:item.complete, overdue:item.overdue}" @click="clickItem(item)">{{item.label}}</div>
|
||||||
|
<Icon class="item-icon" type="ios-locate-outline" @click="scrollPosition(key)"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div ref="ganttRight" class="gantt-right">
|
||||||
|
<div class="gantt-chart">
|
||||||
|
<ul class="gantt-month">
|
||||||
|
<li v-for="(item, key) in monthNum" :key="key" :style="monthStyle(key)">
|
||||||
|
<div class="month-format">{{monthFormat(key)}}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="gantt-date" @mousedown="dateMouseDown">
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul ref="ganttTimeline"
|
||||||
|
class="gantt-timeline"
|
||||||
|
@scroll="timelineScrollListener"
|
||||||
|
@mouseenter="mouseType='timeline'">
|
||||||
|
<li v-for="(item, key) in lists" :key="key">
|
||||||
|
<div
|
||||||
|
class="timeline-item"
|
||||||
|
:style="itemStyle(item)"
|
||||||
|
@mousedown="itemMouseDown($event, item)">
|
||||||
|
<div class="timeline-title">{{item.label}}</div>
|
||||||
|
<div class="timeline-resizer"></div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'GanttView',
|
||||||
|
props: {
|
||||||
|
lists: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
menuWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
},
|
||||||
|
itemWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mouseType: '',
|
||||||
|
mouseWidth: 0,
|
||||||
|
mouseScaleWidth: 0,
|
||||||
|
|
||||||
|
dateWidth: 100,
|
||||||
|
ganttWidth: 0,
|
||||||
|
|
||||||
|
mouseItem: null,
|
||||||
|
mouseBak: {},
|
||||||
|
|
||||||
|
dateMove: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.dateWidth = this.itemWidth;
|
||||||
|
this.$refs.ganttRight.addEventListener('mousewheel', this.handleScroll, false);
|
||||||
|
document.addEventListener('mousemove', this.itemMouseMove);
|
||||||
|
document.addEventListener('mouseup', this.itemMouseUp);
|
||||||
|
window.addEventListener("resize", this.handleResize, false);
|
||||||
|
this.handleResize();
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$refs.ganttRight.removeEventListener('mousewheel', this.handleScroll, false);
|
||||||
|
document.removeEventListener('mousemove', this.itemMouseMove);
|
||||||
|
document.removeEventListener('mouseup', this.itemMouseUp);
|
||||||
|
window.removeEventListener("resize", this.handleResize, false);
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
itemWidth(val) {
|
||||||
|
this.dateWidth = val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
monthNum() {
|
||||||
|
const {ganttWidth, dateWidth} = this;
|
||||||
|
return Math.floor(ganttWidth / dateWidth / 30) + 2
|
||||||
|
},
|
||||||
|
monthStyle() {
|
||||||
|
const {mouseWidth, dateWidth} = this;
|
||||||
|
return function(index) {
|
||||||
|
let mouseDay = mouseWidth == 0 ? 0 : mouseWidth / dateWidth;
|
||||||
|
let date = new Date();
|
||||||
|
//今天00:00:00
|
||||||
|
let nowDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
||||||
|
//当前时间
|
||||||
|
let curDay = new Date(nowDay.getTime() + mouseDay * 86400000);
|
||||||
|
//当月最后一天
|
||||||
|
let lastDay = new Date(curDay.getFullYear(), curDay.getMonth() + 1, 0, 23, 59, 59);
|
||||||
|
//相差天数
|
||||||
|
let diffDay = (lastDay - curDay) / 1000 / 60 / 60 / 24;
|
||||||
|
//
|
||||||
|
let width = dateWidth * diffDay;
|
||||||
|
if (index > 0) {
|
||||||
|
lastDay = new Date(curDay.getFullYear(), curDay.getMonth() + 1 + index, 0);
|
||||||
|
width = lastDay.getDate() * dateWidth;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
width: width + 'px',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
monthFormat() {
|
||||||
|
const {mouseWidth, dateWidth} = this;
|
||||||
|
return function(index) {
|
||||||
|
let mouseDay = mouseWidth == 0 ? 0 : mouseWidth / dateWidth;
|
||||||
|
let date = new Date();
|
||||||
|
//开始位置时间(今天00:00:00)
|
||||||
|
let nowDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
||||||
|
//当前时间
|
||||||
|
let curDay = new Date(nowDay.getTime() + mouseDay * 86400000);
|
||||||
|
//
|
||||||
|
if (index > 0) {
|
||||||
|
curDay = new Date(curDay.getFullYear(), curDay.getMonth() + 1 + index, 0);
|
||||||
|
}
|
||||||
|
return $A.formatDate("Y-m", curDay)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dateNum() {
|
||||||
|
const {ganttWidth, dateWidth} = this;
|
||||||
|
return Math.floor(ganttWidth / dateWidth) + 2
|
||||||
|
},
|
||||||
|
dateStyle() {
|
||||||
|
const {mouseWidth, dateWidth} = this;
|
||||||
|
return function(index) {
|
||||||
|
const style = {};
|
||||||
|
//
|
||||||
|
let mouseDay = mouseWidth == 0 ? 0 : mouseWidth / dateWidth;
|
||||||
|
let mouseData = Math.floor(mouseDay) + index;
|
||||||
|
if (mouseDay == Math.floor(mouseDay)) {
|
||||||
|
mouseData--;
|
||||||
|
}
|
||||||
|
let j = mouseWidth == 0 ? index - 1 : mouseData;
|
||||||
|
let date = new Date(new Date().getTime() + j * 86400000);
|
||||||
|
if ([0, 6].indexOf(date.getDay()) !== -1) {
|
||||||
|
style.backgroundColor = '#f9fafb';
|
||||||
|
}
|
||||||
|
//
|
||||||
|
let width = dateWidth;
|
||||||
|
if (index == 0) {
|
||||||
|
width = Math.abs((mouseWidth % width - width) % width);
|
||||||
|
}
|
||||||
|
style.width = width + 'px';
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dateFormat() {
|
||||||
|
const {mouseWidth, dateWidth} = this;
|
||||||
|
return function(index, type) {
|
||||||
|
let mouseDay = mouseWidth == 0 ? 0 : mouseWidth / dateWidth;
|
||||||
|
let mouseData = Math.floor(mouseDay) + index;
|
||||||
|
if (mouseDay == Math.floor(mouseDay)) {
|
||||||
|
mouseData--;
|
||||||
|
}
|
||||||
|
let j = mouseWidth == 0 ? index - 1 : mouseData;
|
||||||
|
let date = new Date(new Date().getTime() + j * 86400000)
|
||||||
|
if (type == 'day') {
|
||||||
|
return date.getDate();
|
||||||
|
} else if (type == 'wook') {
|
||||||
|
return this.$L(`星期${'日一二三四五六'.charAt(date.getDay())}`);
|
||||||
|
} else {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemStyle() {
|
||||||
|
const {mouseWidth, dateWidth, ganttWidth} = this;
|
||||||
|
return function(item) {
|
||||||
|
const {start, end} = item.time;
|
||||||
|
const {style, moveX, moveW} = item;
|
||||||
|
let date = new Date();
|
||||||
|
//开始位置时间戳(今天00:00:00时间戳)
|
||||||
|
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 left = dateWidth * diffStartDay + (mouseWidth * -1);
|
||||||
|
let width = dateWidth * (diffEndDay - diffStartDay);
|
||||||
|
if (typeof moveX === "number") {
|
||||||
|
left+= moveX;
|
||||||
|
}
|
||||||
|
if (typeof moveW === "number") {
|
||||||
|
width+= moveW;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
const customStyle = {
|
||||||
|
left: Math.min(Math.max(left, width * -1.2), ganttWidth * 1.2).toFixed(2) + 'px',
|
||||||
|
width: width.toFixed(2) + 'px',
|
||||||
|
};
|
||||||
|
if (left < 0 && Math.abs(left) < width) {
|
||||||
|
customStyle.paddingLeft = Math.abs(left).toFixed(2) + 'px'
|
||||||
|
}
|
||||||
|
if (left + width > ganttWidth && left < ganttWidth) {
|
||||||
|
customStyle.paddingRight = Math.abs(left + width - ganttWidth).toFixed(2) + 'px'
|
||||||
|
}
|
||||||
|
if (typeof style === "object") {
|
||||||
|
return Object.assign(customStyle, style);
|
||||||
|
}
|
||||||
|
return customStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
itemScrollListener(e) {
|
||||||
|
if (this.mouseType == 'timeline') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.ganttTimeline.scrollTop = e.target.scrollTop;
|
||||||
|
},
|
||||||
|
timelineScrollListener(e) {
|
||||||
|
if (this.mouseType == 'item') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.ganttItem.scrollTop = e.target.scrollTop;
|
||||||
|
},
|
||||||
|
handleScroll(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.ctrlKey) {
|
||||||
|
//缩放
|
||||||
|
this.dateWidth = Math.min(600, Math.max(24, this.dateWidth - Math.floor(e.deltaY)));
|
||||||
|
this.mouseWidth = this.ganttWidth / 2 * ((this.dateWidth - 100) / 100) + this.dateWidth / 100 * this.mouseScaleWidth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.deltaY != 0) {
|
||||||
|
const ganttTimeline = this.$refs.ganttTimeline;
|
||||||
|
let newTop = ganttTimeline.scrollTop + e.deltaY;
|
||||||
|
if (newTop < 0) {
|
||||||
|
newTop = 0;
|
||||||
|
} else if (newTop > ganttTimeline.scrollHeight - ganttTimeline.clientHeight) {
|
||||||
|
newTop = ganttTimeline.scrollHeight - ganttTimeline.clientHeight;
|
||||||
|
}
|
||||||
|
if (ganttTimeline.scrollTop != newTop) {
|
||||||
|
this.mouseType = 'timeline';
|
||||||
|
ganttTimeline.scrollTop = newTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.deltaX != 0) {
|
||||||
|
this.mouseWidth+= e.deltaX;
|
||||||
|
this.mouseScaleWidth+= e.deltaX * (100 / this.dateWidth);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleResize() {
|
||||||
|
this.ganttWidth = this.$refs.ganttTimeline.clientWidth;
|
||||||
|
},
|
||||||
|
dateMouseDown(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.mouseItem = null;
|
||||||
|
this.dateMove = {
|
||||||
|
clientX: e.clientX
|
||||||
|
};
|
||||||
|
},
|
||||||
|
itemMouseDown(e, item) {
|
||||||
|
e.preventDefault();
|
||||||
|
let type = 'moveX';
|
||||||
|
if (e.target.className == 'timeline-resizer') {
|
||||||
|
type = 'moveW';
|
||||||
|
}
|
||||||
|
if (typeof item[type] !== "number") {
|
||||||
|
this.$set(item, type, 0);
|
||||||
|
}
|
||||||
|
this.mouseBak = {
|
||||||
|
type: type,
|
||||||
|
clientX: e.clientX,
|
||||||
|
value: item[type],
|
||||||
|
};
|
||||||
|
this.mouseItem = item;
|
||||||
|
this.dateMove = null;
|
||||||
|
},
|
||||||
|
itemMouseMove(e) {
|
||||||
|
if (this.mouseItem != null) {
|
||||||
|
e.preventDefault();
|
||||||
|
var diff = e.clientX - this.mouseBak.clientX;
|
||||||
|
this.$set(this.mouseItem, this.mouseBak.type, this.mouseBak.value + diff);
|
||||||
|
} else if (this.dateMove != null) {
|
||||||
|
e.preventDefault();
|
||||||
|
let moveX = (this.dateMove.clientX - e.clientX) * 5;
|
||||||
|
this.dateMove.clientX = e.clientX;
|
||||||
|
this.mouseWidth+= moveX;
|
||||||
|
this.mouseScaleWidth+= moveX * (100 / this.dateWidth);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemMouseUp(e) {
|
||||||
|
if (this.mouseItem != null) {
|
||||||
|
const {start, end} = this.mouseItem.time;
|
||||||
|
let isM = false;
|
||||||
|
//一个宽度的时间
|
||||||
|
let oneWidthTime = 86400000 / this.dateWidth;
|
||||||
|
//修改起止时间
|
||||||
|
if (typeof this.mouseItem.moveX === "number" && this.mouseItem.moveX != 0) {
|
||||||
|
let moveTime = this.mouseItem.moveX * oneWidthTime;
|
||||||
|
this.$set(this.mouseItem.time, 'start', start + moveTime);
|
||||||
|
this.$set(this.mouseItem.time, 'end', end + moveTime);
|
||||||
|
this.$set(this.mouseItem, 'moveX', 0);
|
||||||
|
isM = true;
|
||||||
|
}
|
||||||
|
//修改结束时间
|
||||||
|
if (typeof this.mouseItem.moveW === "number" && this.mouseItem.moveW != 0) {
|
||||||
|
let moveTime = this.mouseItem.moveW * oneWidthTime;
|
||||||
|
this.$set(this.mouseItem.time, 'end', end + moveTime);
|
||||||
|
this.$set(this.mouseItem, 'moveW', 0);
|
||||||
|
isM = true;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (isM) {
|
||||||
|
this.$emit("on-change", this.mouseItem)
|
||||||
|
} else if (e.target.className == 'timeline-title') {
|
||||||
|
this.clickItem(this.mouseItem);
|
||||||
|
}
|
||||||
|
this.mouseItem = null;
|
||||||
|
} else if (this.dateMove != null) {
|
||||||
|
this.dateMove = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollPosition(pos) {
|
||||||
|
let date = new Date();
|
||||||
|
//今天00:00:00
|
||||||
|
let nowDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
||||||
|
//一个宽度的时间
|
||||||
|
let oneWidthTime = 86400000 / this.dateWidth;
|
||||||
|
//
|
||||||
|
let moveWidth = (this.lists[pos].time.start - nowDay) / oneWidthTime - this.dateWidth - this.mouseWidth;
|
||||||
|
this.mouseWidth+= moveWidth;
|
||||||
|
this.mouseScaleWidth+= moveWidth * (100 / this.dateWidth);
|
||||||
|
},
|
||||||
|
clickItem(item) {
|
||||||
|
this.$emit("on-click", item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
270
resources/assets/js/pages/manage/components/ProjectGantt.vue
Normal file
270
resources/assets/js/pages/manage/components/ProjectGantt.vue
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
<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" />
|
||||||
|
<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>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
<div class="project-gstc-close" @click="$emit('on-close')"><Icon type="md-close" /></div>
|
||||||
|
<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">
|
||||||
|
<Button :loading="editLoad > 0" size="small" type="text" @click="editSubmit(false)">{{$L('取消')}}</Button>
|
||||||
|
<Button :loading="editLoad > 0" size="small" type="primary" @click="editSubmit(true)">{{$L('保存')}}</Button>
|
||||||
|
<Icon type="md-arrow-dropright" class="zoom" @click="editShowInfo=false"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="project-gstc-edit-small">
|
||||||
|
<div class="project-gstc-edit-text" @click="editShowInfo=true">{{$L('未保存计划时间')}}: <span v-if="editData">{{editData.length}}</span></div>
|
||||||
|
<Button :loading="editLoad > 0" size="small" type="text" @click="editSubmit(false)">{{$L('取消')}}</Button>
|
||||||
|
<Button :loading="editLoad > 0" size="small" type="primary" @click="editSubmit(true)">{{$L('保存')}}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GanttView from "./GanttView";
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甘特图
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'ProjectGantt',
|
||||||
|
components: {GanttView },
|
||||||
|
props: {
|
||||||
|
projectLabel: {
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
lineTaskData: {
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
levelList: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loadFinish: false,
|
||||||
|
|
||||||
|
lists: [],
|
||||||
|
|
||||||
|
editColumns: [],
|
||||||
|
editData: [],
|
||||||
|
editShowInfo: false,
|
||||||
|
editLoad: 0,
|
||||||
|
|
||||||
|
filtrProjectId: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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']),
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
projectLabel: {
|
||||||
|
handler() {
|
||||||
|
this.initData();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
verifyLists(item){
|
||||||
|
if (this.filtrProjectId > 0) {
|
||||||
|
if (item.id != this.filtrProjectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.tasks && item.tasks.forEach((taskData) => {
|
||||||
|
let notime = !taskData.start_at || !taskData.end_at;
|
||||||
|
let times = this.getTimeObj(taskData);
|
||||||
|
let start = times.start;
|
||||||
|
let end = times.end;
|
||||||
|
//
|
||||||
|
let color = '#058ce4';
|
||||||
|
if (taskData.complete_at) {
|
||||||
|
color = '#c1c1c1';
|
||||||
|
} else {
|
||||||
|
// 等级颜色
|
||||||
|
this.levelList.some(level => {
|
||||||
|
if (level.level === taskData.level) {
|
||||||
|
color = level.color;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//
|
||||||
|
let tempTime = { start, end };
|
||||||
|
let findData = this.editData.find((t) => { return t.id == taskData.id });
|
||||||
|
if (findData) {
|
||||||
|
findData.backTime = $A.cloneData(tempTime)
|
||||||
|
tempTime = $A.cloneData(findData.newTime);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
this.lists.push({
|
||||||
|
id: taskData.id,
|
||||||
|
label: taskData.name,
|
||||||
|
complete: taskData.complete_at,
|
||||||
|
overdue: taskData.overdue,
|
||||||
|
time: tempTime,
|
||||||
|
notime: notime,
|
||||||
|
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) {
|
||||||
|
//修改时间(变化超过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;
|
||||||
|
} else {
|
||||||
|
this.editData.push({
|
||||||
|
id: item.id,
|
||||||
|
label: item.label,
|
||||||
|
notime: item.notime,
|
||||||
|
backTime,
|
||||||
|
newTime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clickItem(item) {
|
||||||
|
this.$store.dispatch("openTask", item);
|
||||||
|
},
|
||||||
|
|
||||||
|
editSubmit(save) {
|
||||||
|
this.editData&&this.editData.forEach((item) => {
|
||||||
|
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],
|
||||||
|
};
|
||||||
|
this.$store.dispatch("taskUpdate", dataJson).then(({msg}) => {
|
||||||
|
$A.messageSuccess(msg);
|
||||||
|
this.editLoad--;
|
||||||
|
if (typeof successCallback === "function") successCallback();
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
$A.modalError(msg);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.lists.some((task) => {
|
||||||
|
if (task.id == item.id) {
|
||||||
|
this.$set(task, 'time', item.backTime);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.editData = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
getRawTime(taskId) {
|
||||||
|
let times = null;
|
||||||
|
this.lists.some((taskData) => {
|
||||||
|
if (taskData.id == taskId) {
|
||||||
|
times = this.getTimeObj(taskData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return times;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTimeObj(taskData) {
|
||||||
|
let start = $A.Time(taskData.start_at) || $A.Time(taskData.start_at);
|
||||||
|
let end = $A.Time(taskData.start_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.max(end, start + 60);
|
||||||
|
start*= 1000;
|
||||||
|
end*= 1000;
|
||||||
|
return {start, end};
|
||||||
|
},
|
||||||
|
|
||||||
|
tapProject(e) {
|
||||||
|
this.filtrProjectId = $A.runNum(e);
|
||||||
|
this.initData();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -85,6 +85,12 @@
|
|||||||
<div><i class="taskfont"></i></div>
|
<div><i class="taskfont"></i></div>
|
||||||
<div><i class="taskfont"></i></div>
|
<div><i class="taskfont"></i></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="project-gantt-item"
|
||||||
|
:class="{active:projectGanttShow}"
|
||||||
|
@click="projectGanttShow=!projectGanttShow">
|
||||||
|
<Icon type="ios-paper-outline" size="26" />
|
||||||
|
{{ $L('甘特图') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -311,7 +317,15 @@
|
|||||||
<TaskRow v-if="projectParameter('showCompleted')" :list="completedList" open-key="completed" @on-priority="addTaskOpen" showCompleteAt/>
|
<TaskRow v-if="projectParameter('showCompleted')" :list="completedList" open-key="completed" @on-priority="addTaskOpen" showCompleteAt/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="project-table overlay-y" v-if="projectGanttShow">
|
||||||
|
<!-- 甘特图 -->
|
||||||
|
<ProjectGantt v-if="projectGanttShow"
|
||||||
|
@on-close="projectGanttShow=false"
|
||||||
|
:lineData="columnList[0].tasks"
|
||||||
|
:projectLabel="columnList"
|
||||||
|
:lineTaskData="columnList[0].tasks"
|
||||||
|
:levelList="taskPriority"></ProjectGantt>
|
||||||
|
</div>
|
||||||
<!--项目设置-->
|
<!--项目设置-->
|
||||||
<Modal
|
<Modal
|
||||||
v-model="settingShow"
|
v-model="settingShow"
|
||||||
@ -463,6 +477,7 @@ import DrawerOverlay from "../../../components/DrawerOverlay";
|
|||||||
import ProjectWorkflow from "./ProjectWorkflow";
|
import ProjectWorkflow from "./ProjectWorkflow";
|
||||||
import TaskMenu from "./TaskMenu";
|
import TaskMenu from "./TaskMenu";
|
||||||
import TaskDeleted from "./TaskDeleted";
|
import TaskDeleted from "./TaskDeleted";
|
||||||
|
import ProjectGantt from "./ProjectGantt";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ProjectList",
|
name: "ProjectList",
|
||||||
@ -470,7 +485,7 @@ export default {
|
|||||||
TaskMenu,
|
TaskMenu,
|
||||||
ProjectWorkflow,
|
ProjectWorkflow,
|
||||||
DrawerOverlay,
|
DrawerOverlay,
|
||||||
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority, TaskDeleted },
|
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority, TaskDeleted, ProjectGantt},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -517,6 +532,7 @@ export default {
|
|||||||
|
|
||||||
flowInfo: {},
|
flowInfo: {},
|
||||||
flowList: [],
|
flowList: [],
|
||||||
|
projectGanttShow: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -14,3 +14,5 @@
|
|||||||
@import "task-menu";
|
@import "task-menu";
|
||||||
@import "task-priority";
|
@import "task-priority";
|
||||||
@import "team-management";
|
@import "team-management";
|
||||||
|
@import "project-gantt";
|
||||||
|
@import "gantt-view";
|
||||||
|
|||||||
259
resources/assets/sass/pages/components/gantt-view.scss
vendored
Normal file
259
resources/assets/sass/pages/components/gantt-view.scss
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
.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: 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-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;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
135
resources/assets/sass/pages/components/project-gantt.scss
vendored
Normal file
135
resources/assets/sass/pages/components/project-gantt.scss
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
.project-gstc-gantt {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: 15px;
|
||||||
|
right: 15px;
|
||||||
|
bottom: 15px;
|
||||||
|
z-index: 1;
|
||||||
|
transform: translateZ(0);
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.project-gstc-dropdown-filtr {
|
||||||
|
position: absolute;
|
||||||
|
top: 38px;
|
||||||
|
left: 222px;
|
||||||
|
|
||||||
|
.project-gstc-dropdown-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
&.filtr {
|
||||||
|
color: #058ce4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-gstc-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
i {
|
||||||
|
transform: scale(1) rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #666666;
|
||||||
|
font-size: 28px;
|
||||||
|
transform: scale(0.92);
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-gstc-edit {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 6px;
|
||||||
|
right: 6px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(120%, 0);
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
.project-gstc-edit-info {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-gstc-edit-small {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-gstc-edit-info {
|
||||||
|
display: none;
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 500px;
|
||||||
|
|
||||||
|
.project-gstc-edit-btns {
|
||||||
|
margin: 12px 6px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.ivu-btn {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoom {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #444444;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #57a3f3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-gstc-edit-small {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 6px 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.project-gstc-edit-text {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #444444;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #57a3f3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-btn {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-dropdown-item {
|
||||||
|
&.dropdown-active {
|
||||||
|
color: #058ce4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -240,6 +240,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.project-gantt-item{
|
||||||
|
padding-left:5px;
|
||||||
|
&.active{
|
||||||
|
color: $primary-color;
|
||||||
|
}
|
||||||
|
.force-right{
|
||||||
|
&.act{
|
||||||
|
color: #2A53FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user