Compare commits
6 commits
main
...
feature/ga
Author | SHA1 | Date | |
---|---|---|---|
|
1817a1f958 | ||
|
affaa95e3f | ||
|
7e6d5956ba | ||
|
a1ee90df33 | ||
|
1604f5b4aa | ||
|
6a0427b216 |
4 changed files with 167 additions and 4 deletions
|
@ -22,6 +22,7 @@
|
|||
"highlight.js": "10.7.2",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "2.0.3",
|
||||
"moment": "^2.29.1",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sass": "1.32.13",
|
||||
"snake-case": "3.0.4",
|
||||
|
@ -30,6 +31,7 @@
|
|||
"vue-advanced-cropper": "1.5.2",
|
||||
"vue-drag-resize": "1.5.4",
|
||||
"vue-easymde": "1.4.0",
|
||||
"vue-ganttastic": "^0.9.32",
|
||||
"vue-shortkey": "3.1.7",
|
||||
"vue-smooth-dnd": "0.8.1",
|
||||
"vuex": "3.6.2"
|
||||
|
|
|
@ -16,6 +16,40 @@
|
|||
v-model="params"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<g-gantt-chart
|
||||
:chart-start="dateFrom.toString()"
|
||||
:chart-end="dateTo.toString()"
|
||||
:push-on-overlap="true"
|
||||
row-label-width="0"
|
||||
:row-height="37"
|
||||
:grid="false"
|
||||
@dragend-bar="dragged($event)"
|
||||
:style="{'--day-width': dayWidthPercent + 'px'}"
|
||||
ref="g-gantt-chart-container"
|
||||
>
|
||||
<g-gantt-row
|
||||
v-for="(t, k) in ganttBars"
|
||||
:key="t ? t.id : 'k'+k"
|
||||
label=""
|
||||
bar-start="start"
|
||||
bar-end="end"
|
||||
:bars="t.bars"
|
||||
:highlight-on-hover="true"
|
||||
>
|
||||
<template v-slot:bar-label>
|
||||
<span>
|
||||
{{ t.title }}
|
||||
</span>
|
||||
<router-link :to="{name: 'task.detail', params: {id: t.id}}" class="edit-task-link">
|
||||
<icon icon="pen"/>
|
||||
</router-link>
|
||||
</template>
|
||||
</g-gantt-row>
|
||||
</g-gantt-chart>
|
||||
|
||||
|
||||
<div class="dates">
|
||||
<template v-for="(y, yk) in days">
|
||||
<div :key="yk + 'year'" class="months">
|
||||
|
@ -191,16 +225,30 @@
|
|||
|
||||
<script>
|
||||
import VueDragResize from 'vue-drag-resize'
|
||||
import {GGanttChart, GGanttRow} from 'vue-ganttastic'
|
||||
import EditTask from './edit-task'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import TaskService from '../../services/task'
|
||||
import TaskModel from '../../models/task'
|
||||
import priorities from '../../models/priorities'
|
||||
import PriorityLabel from './partials/priorityLabel'
|
||||
import TaskCollectionService from '../../services/taskCollection'
|
||||
import {mapState} from 'vuex'
|
||||
import Rights from '../../models/rights.json'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup'
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
|
||||
// Source: https://stackoverflow.com/a/11252167/10924593
|
||||
const treatAsUTC = date => {
|
||||
const result = new Date(date)
|
||||
result.setMinutes(result.getMinutes() - result.getTimezoneOffset())
|
||||
return result
|
||||
}
|
||||
|
||||
const daysBetween = (startDate, endDate) => {
|
||||
const millisecondsPerDay = 24 * 60 * 60 * 1000;
|
||||
return (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'GanttChart',
|
||||
|
@ -209,6 +257,8 @@ export default {
|
|||
PriorityLabel,
|
||||
EditTask,
|
||||
VueDragResize,
|
||||
GGanttChart,
|
||||
GGanttRow,
|
||||
},
|
||||
props: {
|
||||
listId: {
|
||||
|
@ -251,6 +301,8 @@ export default {
|
|||
taskCollectionService: TaskCollectionService,
|
||||
showTaskFilter: false,
|
||||
|
||||
ganttBars: [],
|
||||
|
||||
params: {
|
||||
sort_by: ['done', 'id'],
|
||||
order_by: ['asc', 'desc'],
|
||||
|
@ -275,9 +327,21 @@ export default {
|
|||
mounted() {
|
||||
this.buildTheGanttChart()
|
||||
},
|
||||
computed: mapState({
|
||||
canWrite: (state) => state.currentList.maxRight > Rights.READ,
|
||||
}),
|
||||
computed: {
|
||||
...mapState({
|
||||
canWrite: (state) => state.currentList.maxRight > Rights.READ,
|
||||
}),
|
||||
dayWidthPercent() {
|
||||
const days = daysBetween(this.startDate, this.endDate)
|
||||
|
||||
let containerWidth = 1
|
||||
if(this.$refs['g-gantt-chart-container']) {
|
||||
containerWidth = this.$refs['g-gantt-chart-container'].$el.clientWidth
|
||||
}
|
||||
|
||||
return Math.round(containerWidth / days)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
buildTheGanttChart() {
|
||||
this.setDates()
|
||||
|
@ -360,6 +424,25 @@ export default {
|
|||
if (a.startDate > b.startDate) return 1
|
||||
return 0
|
||||
})
|
||||
|
||||
this.ganttBars = this.theTasks.map(t => {
|
||||
return {
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
bars: [{
|
||||
id: t.id,
|
||||
start: t.startDate.toString(),
|
||||
end: t.endDate.toString(),
|
||||
label: t.title,
|
||||
ganttBarConfig: {
|
||||
color: colorIsDark(t.getHexColor()) ? 'black' : 'white',
|
||||
backgroundColor: t.getHexColor(),
|
||||
handles: true,
|
||||
},
|
||||
}],
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e, this)
|
||||
|
@ -487,6 +570,14 @@ export default {
|
|||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
dragged(e) {
|
||||
const bar = e.movedBars.entries().next().value[0]
|
||||
const index = this.theTasks.findIndex(t => t.id === bar.id)
|
||||
const task = this.theTasks[index]
|
||||
task.startDate = new Date(bar.start)
|
||||
task.endDate = new Date(bar.end)
|
||||
this.$set(this.theTasks, index, task)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -234,4 +234,64 @@ $gantt-vertical-border-color: $grey-100;
|
|||
|
||||
.vdr.active::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
--day-width: 50px;
|
||||
}
|
||||
|
||||
#g-gantt-chart {
|
||||
* {
|
||||
font-family: $family-sans-serif !important;
|
||||
}
|
||||
|
||||
|
||||
.g-gantt-row {
|
||||
&:nth-child(even) {
|
||||
background: rgba(0, 0, 0, 0) repeating-linear-gradient(90deg, #ededed, #ededed 1px, #fff 1px, #fff var(--day-width)) repeat scroll 0% 0%;
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background: rgba(0, 0, 0, 0) repeating-linear-gradient(90deg, #ededed, #ededed 1px, #fafafa 1px, #fafafa var(--day-width)) repeat scroll 0% 0%;
|
||||
}
|
||||
|
||||
> .g-gantt-row-bars-container {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.g-gantt-bar {
|
||||
border-radius: 6px !important;
|
||||
overflow: visible !important;
|
||||
cursor: grab;
|
||||
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.g-gantt-bar-label {
|
||||
font-size: .85rem;
|
||||
justify-content: space-between;
|
||||
padding: 0 .5rem !important;
|
||||
overflow: hidden;
|
||||
|
||||
.edit-task-link {
|
||||
color: inherit;
|
||||
padding: 0 .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.g-gantt-bar-handle-left, .g-gantt-bar-handle-right {
|
||||
width: 8px !important;
|
||||
height: 8px !important;
|
||||
opacity: 1 !important;
|
||||
border-radius: 0 !important;
|
||||
margin-left: -2px !important;
|
||||
border: 1px solid $grey-700 !important;
|
||||
}
|
||||
|
||||
.g-gantt-bar-handle-right {
|
||||
margin-right: -2px !important;
|
||||
}
|
||||
}
|
||||
}
|
10
yarn.lock
10
yarn.lock
|
@ -9543,6 +9543,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.0:
|
|||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
moment@^2.29.1:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
@ -13340,6 +13345,11 @@ vue-flatpickr-component@8.1.6:
|
|||
dependencies:
|
||||
flatpickr "^4.6.6"
|
||||
|
||||
vue-ganttastic@^0.9.32:
|
||||
version "0.9.32"
|
||||
resolved "https://registry.yarnpkg.com/vue-ganttastic/-/vue-ganttastic-0.9.32.tgz#7dc37db63fddd39413444a6674c85d3300f552af"
|
||||
integrity sha512-xVWOFXwBFJ/1piQyQ842RZtpEIRZWOjfhSZeQUTX5oGWskRe1P7yOdZ6SI30x+S/kNGVICcyfawbnMHNix/TYg==
|
||||
|
||||
vue-hot-reload-api@^2.3.0:
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
|
||||
|
|
Loading…
Reference in a new issue