mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-16 22:22:49 +00:00
perf: 优化甘特图移动端交互
This commit is contained in:
parent
d348871b0c
commit
bc7874a3a0
@ -1,28 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-gantt">
|
<div class="common-gantt">
|
||||||
<div class="gantt-left" :style="{width:menuWidth+'px'}">
|
<div class="gantt-left" :style="leftStyle">
|
||||||
<div class="gantt-title">
|
<div class="gantt-title">
|
||||||
<div class="gantt-title-text">{{$L('任务名称')}}</div>
|
<div class="gantt-title-text">{{$L('任务名称')}}</div>
|
||||||
|
<div class="gantt-title-right"><slot name="titleTool"/></div>
|
||||||
</div>
|
</div>
|
||||||
<ul ref="ganttItem"
|
<ul ref="ganttItem"
|
||||||
class="gantt-item"
|
class="gantt-item"
|
||||||
@scroll="itemScrollListener"
|
@scroll="itemScrollListener"
|
||||||
@mouseenter="mouseType='item'">
|
@mouseenter="mouseType='item'">
|
||||||
<li v-for="(item, key) in lists" :key="key">
|
<li v-for="(item, key) in lists" :key="key" @click="clickItem(item, key)">
|
||||||
<div v-if="item.overdue" class="item-overdue" @click="clickItem(item)">{{$L('已超期')}}</div>
|
<div v-if="item.overdue" class="item-overdue">{{$L('已超期')}}</div>
|
||||||
<div class="item-title" :class="{complete:item.complete, overdue:item.overdue}" @click="clickItem(item)">{{item.label}}</div>
|
<div class="item-title" :class="{complete:item.complete, overdue:item.overdue}">{{item.label}}</div>
|
||||||
<Icon class="item-icon" type="ios-locate-outline" @click="scrollPosition(key)"/>
|
<Icon class="item-icon" type="ios-locate-outline" @click.stop="scrollPosition(key)"/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div ref="ganttRight" class="gantt-right">
|
<div ref="ganttRight" class="gantt-right">
|
||||||
|
<div class="gantt-size" @click="maximize=!maximize">
|
||||||
|
<i v-if="maximize" class="taskfont"></i>
|
||||||
|
<i v-else class="taskfont"></i>
|
||||||
|
</div>
|
||||||
<div class="gantt-chart">
|
<div class="gantt-chart">
|
||||||
<ul class="gantt-month">
|
<ul class="gantt-month">
|
||||||
<li v-for="(item, key) in monthNum" :key="key" :style="monthStyle(key)">
|
<li v-for="(item, key) in monthNum" :key="key" :style="monthStyle(key)">
|
||||||
<div class="month-format">{{monthFormat(key)}}</div>
|
<div class="month-format">{{monthFormat(key)}}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="gantt-date" @mousedown="dateMouseDown">
|
<ul class="gantt-date"
|
||||||
|
@touchstart="dateTouchstart"
|
||||||
|
@touchmove="dateTouchmove"
|
||||||
|
@touchend="dateTouchend"
|
||||||
|
@mousedown="dateMouseDown">
|
||||||
<li v-for="(item, key) in dateNum" :key="key" :style="dateStyle(key)">
|
<li v-for="(item, key) in dateNum" :key="key" :style="dateStyle(key)">
|
||||||
<div class="date-format">
|
<div class="date-format">
|
||||||
<div class="format-day">{{dateFormat(key, 'day')}}</div>
|
<div class="format-day">{{dateFormat(key, 'day')}}</div>
|
||||||
@ -77,10 +86,13 @@ export default {
|
|||||||
mouseItem: null,
|
mouseItem: null,
|
||||||
mouseBak: {},
|
mouseBak: {},
|
||||||
|
|
||||||
dateMove: null
|
dateMove: null,
|
||||||
|
|
||||||
|
maximize: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.maximize = this.windowPortrait;
|
||||||
this.dateWidth = this.itemWidth;
|
this.dateWidth = this.itemWidth;
|
||||||
this.$refs.ganttRight.addEventListener('mousewheel', this.handleScroll, false);
|
this.$refs.ganttRight.addEventListener('mousewheel', this.handleScroll, false);
|
||||||
document.addEventListener('mousemove', this.itemMouseMove);
|
document.addEventListener('mousemove', this.itemMouseMove);
|
||||||
@ -97,9 +109,21 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
itemWidth(val) {
|
itemWidth(val) {
|
||||||
this.dateWidth = val;
|
this.dateWidth = val;
|
||||||
|
},
|
||||||
|
maximize() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.handleResize();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
leftStyle({menuWidth, maximize}) {
|
||||||
|
const style = {width: menuWidth + 'px'}
|
||||||
|
if (maximize) {
|
||||||
|
style.display = 'none';
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
},
|
||||||
monthNum() {
|
monthNum() {
|
||||||
const {ganttWidth, dateWidth} = this;
|
const {ganttWidth, dateWidth} = this;
|
||||||
return Math.floor(ganttWidth / dateWidth / 30) + 2
|
return Math.floor(ganttWidth / dateWidth / 30) + 2
|
||||||
@ -271,6 +295,31 @@ export default {
|
|||||||
handleResize() {
|
handleResize() {
|
||||||
this.ganttWidth = this.$refs.ganttTimeline.clientWidth;
|
this.ganttWidth = this.$refs.ganttTimeline.clientWidth;
|
||||||
},
|
},
|
||||||
|
dateTouchstart(e) {
|
||||||
|
if (this.windowPortrait) {
|
||||||
|
this.maximize = true
|
||||||
|
}
|
||||||
|
this.mouseItem = null;
|
||||||
|
this.dateMove = {
|
||||||
|
clientX: e.touches[0].clientX
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dateTouchmove(e) {
|
||||||
|
if (this.mouseItem != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.dateMove != null) {
|
||||||
|
let moveX = (this.dateMove.clientX - e.touches[0].clientX) * 5;
|
||||||
|
this.dateMove.clientX = e.touches[0].clientX;
|
||||||
|
this.mouseWidth+= moveX;
|
||||||
|
this.mouseScaleWidth+= moveX * (100 / this.dateWidth);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dateTouchend() {
|
||||||
|
if (this.dateMove != null) {
|
||||||
|
this.dateMove = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
dateMouseDown(e) {
|
dateMouseDown(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.mouseItem = null;
|
this.mouseItem = null;
|
||||||
@ -308,7 +357,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$set(this.mouseItem, this.mouseBak.type, diff);
|
this.$set(this.mouseItem, this.mouseBak.type, diff);
|
||||||
} else if (this.dateMove != null) {
|
return;
|
||||||
|
}
|
||||||
|
if (this.dateMove != null) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let moveX = (this.dateMove.clientX - e.clientX) * 5;
|
let moveX = (this.dateMove.clientX - e.clientX) * 5;
|
||||||
this.dateMove.clientX = e.clientX;
|
this.dateMove.clientX = e.clientX;
|
||||||
@ -344,7 +395,9 @@ export default {
|
|||||||
this.clickItem(this.mouseItem);
|
this.clickItem(this.mouseItem);
|
||||||
}
|
}
|
||||||
this.mouseItem = null;
|
this.mouseItem = null;
|
||||||
} else if (this.dateMove != null) {
|
return
|
||||||
|
}
|
||||||
|
if (this.dateMove != null) {
|
||||||
this.dateMove = null;
|
this.dateMove = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -359,7 +412,11 @@ export default {
|
|||||||
this.mouseWidth+= moveWidth;
|
this.mouseWidth+= moveWidth;
|
||||||
this.mouseScaleWidth+= moveWidth * (100 / this.dateWidth);
|
this.mouseScaleWidth+= moveWidth * (100 / this.dateWidth);
|
||||||
},
|
},
|
||||||
clickItem(item) {
|
clickItem(item, key = undefined) {
|
||||||
|
if (key !== undefined && this.windowPortrait) {
|
||||||
|
this.scrollPosition(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$emit("on-click", item)
|
this.$emit("on-click", item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,24 +5,27 @@
|
|||||||
:menuWidth="menuWidth"
|
:menuWidth="menuWidth"
|
||||||
:itemWidth="80"
|
:itemWidth="80"
|
||||||
@on-change="onChange"
|
@on-change="onChange"
|
||||||
@on-click="onClick"/>
|
@on-click="onClick">
|
||||||
<Dropdown class="project-gstc-dropdown-filtr" :style="dropStyle" trigger="click" @on-click="onSwitchColumn">
|
<template #titleTool>
|
||||||
<Icon class="project-gstc-dropdown-icon" :class="{filtr:filtrProjectId > 0}" type="md-funnel" />
|
<Dropdown class="project-gstc-dropdown-filtr" trigger="click" @on-click="onSwitchColumn">
|
||||||
<DropdownMenu slot="list">
|
<Icon class="project-gstc-dropdown-icon" :class="{filtr:filtrProjectId > 0}" type="md-funnel" />
|
||||||
<DropdownItem :name="0" :class="{'dropdown-active':filtrProjectId == 0}">{{ $L('全部') }}</DropdownItem>
|
<DropdownMenu slot="list">
|
||||||
<DropdownItem
|
<DropdownItem :name="0" :class="{'dropdown-active':filtrProjectId == 0}">{{ $L('全部') }}</DropdownItem>
|
||||||
v-for="(item, index) in projectColumn"
|
<DropdownItem
|
||||||
:key="index"
|
v-for="(item, index) in projectColumn"
|
||||||
:name="item.id"
|
:key="index"
|
||||||
:class="{'dropdown-active':filtrProjectId == item.id}">
|
:name="item.id"
|
||||||
{{ item.name }}
|
:class="{'dropdown-active':filtrProjectId == item.id}">
|
||||||
<span v-if="item.tasks">({{ filtrLength(item.tasks) }})</span>
|
{{ item.name }}
|
||||||
</DropdownItem>
|
<span v-if="item.tasks">({{ filtrLength(item.tasks) }})</span>
|
||||||
</DropdownMenu>
|
</DropdownItem>
|
||||||
</Dropdown>
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
</template>
|
||||||
|
</GanttView>
|
||||||
<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">
|
<div class="project-gstc-edit-info">
|
||||||
<Table size="small" max-height="600" :columns="editColumns" :data="editData"></Table>
|
<Table max-height="600" :columns="editColumns" :data="editData"></Table>
|
||||||
<div class="project-gstc-edit-btns">
|
<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="text" @click="editSubmit(false)">{{$L('取消')}}</Button>
|
||||||
<Button :loading="editLoad > 0" size="small" type="primary" @click="editSubmit(true)">{{$L('保存')}}</Button>
|
<Button :loading="editLoad > 0" size="small" type="primary" @click="editSubmit(true)">{{$L('保存')}}</Button>
|
||||||
@ -114,10 +117,6 @@ export default {
|
|||||||
return this.windowWidth < 1440 ? 180 : 260;
|
return this.windowWidth < 1440 ? 180 : 260;
|
||||||
},
|
},
|
||||||
|
|
||||||
dropStyle() {
|
|
||||||
return this.windowWidth < 1440 ? {left: '142px'} : {};
|
|
||||||
},
|
|
||||||
|
|
||||||
completedTask() {
|
completedTask() {
|
||||||
return this.projectData.cacheParameter.completedTask;
|
return this.projectData.cacheParameter.completedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
33
resources/assets/sass/components/gantt-view.scss
vendored
33
resources/assets/sass/components/gantt-view.scss
vendored
@ -33,21 +33,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gantt-title {
|
.gantt-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
height: 76px;
|
height: 76px;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-color: #F9FAFB;
|
background-color: #F9FAFB;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
padding-top: 26px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.gantt-title-text {
|
.gantt-title-text {
|
||||||
line-height: 100px;
|
flex: 1;
|
||||||
max-width: 200px;
|
line-height: 22px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gantt-title-right {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gantt-item {
|
.gantt-item {
|
||||||
@ -122,6 +129,28 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
.gantt-size {
|
||||||
|
position: absolute;
|
||||||
|
top: 76px;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.502);
|
||||||
|
transition: all 250ms;
|
||||||
|
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
|
border-bottom-right-radius: 9px;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
> i {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.gantt-chart {
|
.gantt-chart {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@ -11,9 +11,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.project-gstc-dropdown-filtr {
|
.project-gstc-dropdown-filtr {
|
||||||
position: absolute;
|
padding: 0 16px;
|
||||||
top: 38px;
|
|
||||||
left: 222px;
|
|
||||||
|
|
||||||
.project-gstc-dropdown-icon {
|
.project-gstc-dropdown-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user