2
0
mirror of https://github.com/tenrok/vue-ganttastic.git synced 2026-06-21 07:00:34 +03:00

feat: add timeaxis mode: [month_days, day_hours]

This commit is contained in:
yicone
2021-02-02 11:54:10 +08:00
parent ad91261809
commit 1e1ec86bfa
4 changed files with 266 additions and 226 deletions
+5 -2
View File
@@ -62,7 +62,9 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
defaultBarLength: { type: Number, default: 1 } defaultBarLength: { type: Number, default: 1 },
// ["month_days", "day_hours"]
timeaxisMode: {type: String, default: "month_days"}
}, },
data(){ data(){
@@ -297,7 +299,8 @@ export default {
shouldSnapBackOnOverlap: () => this.snapBackOnOverlap, shouldSnapBackOnOverlap: () => this.snapBackOnOverlap,
snapBackBundle: (ganttBar) => this.snapBackBundle(ganttBar), snapBackBundle: (ganttBar) => this.snapBackBundle(ganttBar),
getMinGapBetweenBars: () => this.minGapBetweenBars, getMinGapBetweenBars: () => this.minGapBetweenBars,
getDefaultBarLength: () => this.defaultBarLength getDefaultBarLength: () => this.defaultBarLength,
getTimeaxisMode:() => this.timeaxisMode
} }
} }
} }
+41 -36
View File
@@ -1,17 +1,17 @@
<template> <template>
<div <div
class="g-grid-container" class="g-grid-container"
:style="{ :style="{
left: rowLabelWidth, left: rowLabelWidth,
width: `${100-(this.rowLabelWidth).replace('%','')}%` width: `${100 - this.rowLabelWidth.replace('%', '')}%`,
}" }"
> >
<div <div
v-for="(hour,index) in allHours" v-for="(hour, index) in allHours"
:key="index" :key="index"
:class="{ :class="{
'g-grid-line': true, 'g-grid-line': true,
'g-grid-line-highlighted': highlightedHours.includes(hour) 'g-grid-line-highlighted': highlightedHours.includes(hour),
}" }"
/> />
</div> </div>
@@ -21,52 +21,57 @@
import moment from 'moment' import moment from 'moment'
export default { export default {
name: 'GGanttGrid',
inject: ['getTimeaxisMode'],
name: "GGanttGrid",
props: { props: {
chartStart: {type: String}, chartStart: { type: String },
chartEnd: {type: String}, chartEnd: { type: String },
rowLabelWidth: String, rowLabelWidth: String,
highlightedHours: {type: Array, default: () => []} highlightedHours: { type: Array, default: () => [] },
}, },
computed: { computed: {
allHours(){ allHours() {
let momentChartStart = moment(this.chartStart) let momentChartStart = moment(this.chartStart)
let momentChartEnd = moment(this.chartEnd) let momentChartEnd = moment(this.chartEnd)
let res = [] let res = []
while(momentChartStart.isSameOrBefore(momentChartEnd)){ const timeaxisMode = this.getTimeaxisMode()
res.push(momentChartStart.date()) while (momentChartStart.isSameOrBefore(momentChartEnd)) {
momentChartStart.add(1,"day") if (timeaxisMode === 'month_days') {
res.push(momentChartStart.date())
momentChartStart.add(1, 'day')
} else if (timeaxisMode === 'day_hours') {
res.push(momentChartStart.date())
momentChartStart.add(1, 'hour')
}
} }
return res return res
} },
},
}
} }
</script> </script>
<style scoped> <style scoped>
.g-grid-container{ .g-grid-container {
position: absolute; position: absolute;
top: 0; top: 0;
left: 30%; /* must correspond to width of row title */ left: 30%; /* must correspond to width of row title */
width: 70%; width: 70%;
height: calc(100% - 23px); height: calc(100% - 23px);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.g-grid-line{ .g-grid-line {
width: 1px; width: 1px;
height: 100%; height: 100%;
background: #eaeaea; background: #eaeaea;
} }
.g-grid-line-highlighted{ .g-grid-line-highlighted {
background: #90CAF9; background: #90caf9;
box-shadow: 0px 0px 0px 1px #90CAF9; box-shadow: 0px 0px 0px 1px #90caf9;
} }
</style> </style>
+2
View File
@@ -109,7 +109,9 @@ export default {
onDragover(e) { onDragover(e) {
e.preventDefault() // enables dropping content on row e.preventDefault() // enables dropping content on row
if(this.highlightOnHover){ if(this.highlightOnHover){
console.log({backgroundColor: this.$refs["g-gantt-row"].style.backgroundColor })
this.$refs["g-gantt-row"].style.backgroundColor = this.getThemeColors().hoverHighlight this.$refs["g-gantt-row"].style.backgroundColor = this.getThemeColors().hoverHighlight
console.log({backgroundColor: this.$refs["g-gantt-row"].style.backgroundColor })
} }
}, },
+218 -188
View File
@@ -1,61 +1,51 @@
<template> <template>
<div id="g-timeaxis"> <div id="g-timeaxis">
<div <div
class="g-timeaxis-empty-space" class="g-timeaxis-empty-space"
:style="{width: rowLabelWidth, background: themeColors.secondary}" :style="{ width: rowLabelWidth, background: themeColors.secondary }"
/> />
<div <div
class="g-timeaxis-days" class="g-timeaxis-days"
:style="{width: `${100-rowLabelWidth.replace('%','')}%`}" :style="{ width: `${100 - rowLabelWidth.replace('%', '')}%` }"
> >
<div <div
v-for="(month, index) in axisMonths" v-for="(point, index) in axisPoints"
:key="month.text" :key="point.text"
class="g-timeaxis-day" class="g-timeaxis-day"
:style="{ :style="{
width: month.widthPercentage+'%', width: point.widthPercentage + '%',
background: index%2===0 ? themeColors.primary : themeColors.secondary, background:
color: themeColors.text index % 2 === 0 ? themeColors.primary : themeColors.secondary,
}" color: themeColors.text,
}"
>
<div>{{ pointFormatted(point) }}</div>
<div
:style="{ background: themeColors.ternary, color: themeColors.text }"
> >
<div> {{monthFormatted(month)}} </div> <div
<div :style="{background: themeColors.ternary, color: themeColors.text}"> v-for="(childPoint, index) in point.ganttDays"
<div :key="childPoint.fullDatetime"
v-for="(day, index) in month.ganttDays"
:key="day.fullDatetime"
class="g-timeaxis-hour" class="g-timeaxis-hour"
:style="{ :style="{
width: day.widthPercentage+'%', width: childPoint.widthPercentage + '%',
background: index%2===0 ? themeColors.primary : themeColors.secondary, background:
color: themeColors.text index % 2 === 0 ? themeColors.primary : themeColors.secondary,
color: themeColors.text,
}" }"
> >
<span :style="{fontSize: hourFontSize}">{{day.text}}</span> <span :style="{ fontSize: hourFontSize }">{{
<div childPoint.text
}}</span>
<div
class="g-timeaxis-hour-pin" class="g-timeaxis-hour-pin"
:style="{background: themeColors.text}" :style="{ background: themeColors.text }"
/> />
<!-- <div> {{dayFormatted(day)}} </div>
<div :style="{background: themeColors.ternary, color: themeColors.text}">
<div
v-for="hour in day.ganttHours"
:key="hour.fullDatetime"
class="g-timeaxis-hour"
>
<span :style="{fontSize: hourFontSize}">{{hour.text}}</span>
<div
class="g-timeaxis-hour-pin"
:style="{background: themeColors.text}"
/>
</div>
</div> -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="g-timeaxis-marker"/> <div id="g-timeaxis-marker" />
</div> </div>
</template> </template>
@@ -63,219 +53,259 @@
import moment from 'moment' import moment from 'moment'
export default { export default {
name: 'GGanttTimeaxis',
name:"GGanttTimeaxis", inject: ['getTimeaxisMode'],
props: { props: {
chartStart: String, chartStart: String,
chartEnd: String, chartEnd: String,
rowLabelWidth: String, rowLabelWidth: String,
timemarkerOffset: {type: Number, default: 0}, timemarkerOffset: { type: Number, default: 0 },
locale: String, locale: String,
themeColors: Object themeColors: Object,
}, },
data(){ data() {
return { return {
axisMonths: [], axisPoints: [],
axisDays: [], childPointCount: null,
dayCount: null,
hourCount: null,
timemarker: null, timemarker: null,
hourFontSize: "11px", hourFontSize: '11px',
monthFormat: "M月", monthFormat: 'M月',
dayFormat: "MM-DD" dayFormat: 'MM-DD',
mode: this.getTimeaxisMode(),
} }
}, },
mounted(){ mounted() {
this.timemarker = document.querySelector("#g-timeaxis-marker") this.timemarker = document.querySelector('#g-timeaxis-marker')
this.initAxisMonthsAndDays() this.initAxis()
// this.initAxisDaysAndHours()
this.onWindowResize() this.onWindowResize()
window.addEventListener('resize', this.onWindowResize) window.addEventListener('resize', this.onWindowResize)
window.addEventListener("mousemove", (event) => this.moveTimemarker(event)) window.addEventListener('mousemove', (event) => this.moveTimemarker(event))
window.addEventListener("dragover", (event) => this.moveTimemarker(event)) window.addEventListener('dragover', (event) => this.moveTimemarker(event))
}, },
methods: { methods: {
initAxisMonthsAndDays(){ initAxis() {
this.axisMonths = [] if (this.mode === 'month_days') {
let start = moment(this.chartStart) this.initAxisMonthsAndDays()
let end = moment(this.chartEnd) } else if (this.mode === 'day_hours') {
this.dayCount = Math.floor(end.diff(start, "day", true)) this.initAxisDaysAndHours()
while(start.isBefore(end)){
let dayCountOfMonth = start.format("MM.YYYY")==end.format("MM.YYYY") ? end.date() : (start.daysInMonth() - start.date() + 1)
let widthPercentage = dayCountOfMonth/this.dayCount*100
console.log({start, startDay: start.date(), end, endDay: end.date(), dayCountOfMonth, dayCount: this.dayCount, widthPercentage})
let endDay = start.month()===end.month() ? end.date() : end.daysInMonth()
this.axisMonths.push(this.getAxisMonthObject(start, widthPercentage, endDay))
start.add(1,"month").date(1).hour(0)
}
console.log(this.axisMonths)
},
initAxisDaysAndHours(){
this.axisDays = []
let start = moment(this.chartStart)
let end = moment(this.chartEnd)
this.hourCount = Math.floor(end.diff(start, "hour", true))
while(start.isBefore(end)){
let hourCountOfDay = start.format("DD.MM.YYYY")==end.format("DD.MM.YYYY") ? end.hour() : 24-start.hour()
console.log({start, hourCountOfDay, hourCount: this.hourCount})
let widthPercentage = hourCountOfDay/this.hourCount*100
let endHour = start.day()===end.day() ? end.hour()-1 : 23 // -1 because the last hour is not included e.g if chartEnd=04:00 the last interval we display is between 03 and 04
this.axisDays.push(this.getAxisDayObject(start, widthPercentage, endHour))
start.add(1,"day").hour(0)
} }
}, },
getAxisMonthObject(datetime, widthPercentage, endDay){ initAxisMonthsAndDays() {
let start = moment(this.chartStart)
let end = moment(this.chartEnd)
this.childPointCount = Math.floor(end.diff(start, 'day', true))
while (start.isBefore(end)) {
let dayCountOfMonth =
start.isSame(end, 'day')
? end.date()
: start.daysInMonth() - start.date() + 1
let widthPercentage = (dayCountOfMonth / this.childPointCount) * 100
let endDay =
start.month() === end.month() ? end.date() : end.daysInMonth()
this.axisPoints.push(
this.getAxisMonthObject(start, widthPercentage, endDay)
)
start.add(1, 'month').date(1).hour(0)
}
},
initAxisDaysAndHours() {
let start = moment(this.chartStart)
let end = moment(this.chartEnd)
this.childPointCount = Math.floor(end.diff(start, 'hour', true))
while (start.isBefore(end)) {
let hourCountOfDay =
start.isSame(end, 'day')
? end.hour()
: 24 - start.hour()
let widthPercentage = (hourCountOfDay / this.childPointCount) * 100
let endHour = start.day() === end.day() ? end.hour() - 1 : 23 // -1 because the last hour is not included e.g if chartEnd=04:00 the last interval we display is between 03 and 04
this.axisPoints.push(
this.getAxisDayObject(start, widthPercentage, endHour)
)
start.add(1, 'day').hour(0)
}
},
getAxisMonthObject(datetime, widthPercentage, endDay) {
let datetimeMoment = moment(datetime) let datetimeMoment = moment(datetime)
let axisMonthObject = { let axisMonthObject = {
widthPercentage : widthPercentage, widthPercentage: widthPercentage,
value : datetime.format("YYYY-MM"), value: datetime.format('YYYY-MM'),
ganttDays : [] ganttDays: [],
} }
let startDay = datetimeMoment.date() let startDay = datetimeMoment.date()
for(let i=0; i <=(endDay-startDay); i++) { for (let i = 0; i <= endDay - startDay; i++) {
let day ={ let day = {
text: datetimeMoment.format("D日"), text: datetimeMoment.format('D日'),
fullDatetime: datetimeMoment.format("YYYY-MM-DD") fullDatetime: datetimeMoment.format('YYYY-MM-DD'),
} }
axisMonthObject.ganttDays.push(day) axisMonthObject.ganttDays.push(day)
datetimeMoment.add(1,"day") datetimeMoment.add(1, 'day')
} }
return axisMonthObject return axisMonthObject
}, },
getAxisDayObject(datetime, widthPercentage, endHour){ getAxisDayObject(datetime, widthPercentage, endHour) {
let datetimeMoment = moment(datetime) let datetimeMoment = moment(datetime)
let axisDayObject = { let axisDayObject = {
widthPercentage : widthPercentage, widthPercentage: widthPercentage,
value : datetime.format("YYYY-MM-DD"), value: datetime.format('YYYY-MM-DD'),
ganttHours : [] ganttHours: [],
} }
let startHour = datetimeMoment.hour() let startHour = datetimeMoment.hour()
for(let i=0; i <=(endHour-startHour); i++) { for (let i = 0; i <= endHour - startHour; i++) {
let hour ={ let hour = {
text: datetimeMoment.format("HH"), text: datetimeMoment.format('HH'),
fullDatetime: datetimeMoment.format("DD.MM.YYYY HH:mm") fullDatetime: datetimeMoment.format('DD.MM.YYYY HH:mm'),
} }
axisDayObject.ganttHours.push(hour) axisDayObject.ganttHours.push(hour)
datetimeMoment.add(1,"hour") datetimeMoment.add(1, 'hour')
} }
return axisDayObject return axisDayObject
}, },
moveTimemarker(event){ moveTimemarker(event) {
this.timemarker.style.left = (event.clientX - this.timemarkerOffset - this.horizontalAxisContainer.left)+"px" this.timemarker.style.left =
event.clientX -
this.timemarkerOffset -
this.horizontalAxisContainer.left +
'px'
}, },
onWindowResize(){ onWindowResize() {
this.horizontalAxisContainer = document.querySelector("#g-timeaxis").getBoundingClientRect() this.horizontalAxisContainer = document
this.hourFontSize = Math.min(9.5, 0.75*(this.horizontalAxisContainer.width/this.hourCount))+"px" .querySelector('#g-timeaxis')
.getBoundingClientRect()
this.hourFontSize =
Math.min(
9.5,
0.75 * (this.horizontalAxisContainer.width / this.childPointCount)
) + 'px'
}, },
monthFormatted(month){ // do not display month text if the month is smaller than x% pointFormatted(point){
return month.widthPercentage>=1/32*100 ? moment(month.value).locale(this.locale).format(this.monthFormat) : "" if (this.mode === 'month_days') {
return this.monthFormatted(point)
} else if (this.mode === 'day_hours') {
return this.dayFormatted(point)
}
}, },
dayFormatted(day){ // do not display day text if the day is smaller than 12% monthFormatted(month) {
return day.widthPercentage>=12 ? moment(day.value).locale(this.locale).format(this.dayFormat) : "" // do not display month text if the month is smaller than x%
} return month.widthPercentage >= (1 / 32) * 100
? moment(month.value).locale(this.locale).format(this.monthFormat)
: ''
},
dayFormatted(day) {
// do not display day text if the day is smaller than 12%
return day.widthPercentage >= 12
? moment(day.value).locale(this.locale).format(this.dayFormat)
: ''
},
}, },
watch: { watch: {
chartStart(){ chartStart() {
this.initAxisMonthsAndDays() this.initAxis()
// this.initAxisDaysAndHours()
}, },
chartEnd(){ chartEnd() {
this.initAxisMonthsAndDays() this.initAxis()
// this.initAxisDaysAndHours() },
} },
}
} }
</script> </script>
<style scoped> <style scoped>
#g-timeaxis, .g-timeaxis-days, .g-timeaxis-day, .g-timeaxis-day > div { #g-timeaxis,
display: flex; .g-timeaxis-days,
overflow: hidden; .g-timeaxis-day,
} .g-timeaxis-day > div {
display: flex;
overflow: hidden;
}
#g-timeaxis { #g-timeaxis {
position: sticky; position: sticky;
top:0; top: 0;
width: 100%; width: 100%;
height: 8%; height: 8%;
min-height: 75px; min-height: 75px;
background: white; background: white;
z-index: 4; z-index: 4;
box-shadow: 0px 1px 3px 2px rgba(50,50,50, 0.5); box-shadow: 0px 1px 3px 2px rgba(50, 50, 50, 0.5);
} }
#g-timeaxis > .g-timeaxis-empty-space { #g-timeaxis > .g-timeaxis-empty-space {
width: 20%; /* this has to be as wide as .ganttRowTitle in VGanttastic.css */ width: 20%; /* this has to be as wide as .ganttRowTitle in VGanttastic.css */
height: 100%; height: 100%;
background: #F5F5F5; background: #f5f5f5;
} }
#g-timeaxis > .g-timeaxis-days { #g-timeaxis > .g-timeaxis-days {
position: relative; position: relative;
width: 80%; width: 80%;
height: 100%, height: 100%;
} }
.g-timeaxis-day { .g-timeaxis-day {
height: 100%; height: 100%;
flex-direction: column; flex-direction: column;
background: #E0E0E0; background: #e0e0e0;
} }
.g-timeaxis-day:nth-child(odd) { .g-timeaxis-day:nth-child(odd) {
background: #E8E8E8; background: #e8e8e8;
} }
.g-timeaxis-day > div:nth-child(1) { /* day text */ .g-timeaxis-day > div:nth-child(1) {
height: 50%; /* day text */
justify-content: space-around; height: 50%;
font-weight: bold; justify-content: space-around;
align-items: center; font-weight: bold;
} align-items: center;
}
.g-timeaxis-day > div:nth-child(2) { /* hours of a day */ .g-timeaxis-day > div:nth-child(2) {
align-items: flex-end; /* hours of a day */
height: 50%; align-items: flex-end;
justify-content: space-between; height: 50%;
background:#F5F5F5; justify-content: space-between;
padding-top:2px; background: #f5f5f5;
color: #212121; padding-top: 2px;
} color: #212121;
}
.g-timeaxis-hour {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-direction: column;
opacity: 0.5;
width: 100%;
}
.g-timeaxis-hour-pin { .g-timeaxis-hour {
width: 1px; display: flex;
height: 8px; justify-content: space-between;
} align-items: flex-start;
flex-direction: column;
opacity: 0.5;
width: 100%;
}
#g-timeaxis-marker { .g-timeaxis-hour-pin {
position: absolute; width: 1px;
top:0; height: 8px;
left:0; }
height: 100%;
width: 3px; #g-timeaxis-marker {
background: black; position: absolute;
} top: 0;
left: 0;
height: 100%;
width: 3px;
background: black;
}
</style> </style>