You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
11 KiB
349 lines
11 KiB
<template lang="pug">
|
|
svg.vch__wrapper(:viewBox="viewbox")
|
|
g.vch__months__labels__wrapper(:transform="monthsLabelWrapperTransform[position]")
|
|
text.vch__month__label(
|
|
v-for="(month, index) in heatmap.firstFullWeekOfMonths",
|
|
:x="getMonthLabelPostion(month).x",
|
|
:y="getMonthLabelPostion(month).y"
|
|
) {{ lo.months[month.value] }}
|
|
|
|
g.vch__days__labels__wrapper(:transform="daysLabelWrapperTransform[position]")
|
|
//- text.vch__day__label(
|
|
//- :x="vertical ? SQUARE_SIZE * 0 : 0",
|
|
//- :y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 8"
|
|
//- ) {{ lo.days[0] }}
|
|
text.vch__day__label(
|
|
:x="vertical ? SQUARE_SIZE * 1 : 0",
|
|
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 20"
|
|
) {{ lo.days[1] }}
|
|
//- text.vch__day__label(
|
|
//- :x="vertical ? SQUARE_SIZE * 2 : 0",
|
|
//- :y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 32"
|
|
//- ) {{ lo.days[2] }}
|
|
text.vch__day__label(
|
|
:x="vertical ? SQUARE_SIZE * 3 : 0",
|
|
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 44"
|
|
) {{ lo.days[3] }}
|
|
//- text.vch__day__label(
|
|
//- :x="vertical ? SQUARE_SIZE * 4 : 0",
|
|
//- :y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 56"
|
|
//- ) {{ lo.days[4] }}
|
|
text.vch__day__label(
|
|
:x="vertical ? SQUARE_SIZE * 5 : 0",
|
|
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 69"
|
|
) {{ lo.days[5] }}
|
|
//- text.vch__day__label(
|
|
//- :x="vertical ? SQUARE_SIZE * 6 : 0",
|
|
//- :y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 81"
|
|
//- ) {{ lo.days[6] }}
|
|
|
|
g.vch__legend__wrapper(:transform="legendWrapperTransform[position]")
|
|
text(
|
|
:x="vertical ? SQUARE_SIZE * 1.25 : -25"
|
|
:y="vertical ? 8 : SQUARE_SIZE + 1"
|
|
) {{ lo.less }}
|
|
rect(
|
|
v-for="(color, index) in rangeColor",
|
|
:key="index",
|
|
:style="{ fill: color }",
|
|
:width="SQUARE_SIZE - SQUARE_BORDER_SIZE",
|
|
:height="SQUARE_SIZE - SQUARE_BORDER_SIZE",
|
|
:x="vertical ? SQUARE_SIZE * 1.75 : SQUARE_SIZE * index",
|
|
:y="vertical ? SQUARE_SIZE * (index + 1) : 5"
|
|
)
|
|
text(
|
|
:x="vertical ? SQUARE_SIZE * 1.25 : SQUARE_SIZE * rangeColor.length + 1",
|
|
:y="vertical ? SQUARE_SIZE * (rangeColor.length + 2) - SQUARE_BORDER_SIZE : SQUARE_SIZE + 1"
|
|
) {{ lo.more }}
|
|
g.vch__year__wrapper(:transform="yearWrapperTransform")
|
|
g.vch__month__wrapper(
|
|
v-for="(week, weekIndex) in heatmap.calendar",
|
|
:key="weekIndex",
|
|
:transform="getWeekPosition(weekIndex)"
|
|
)
|
|
rect.vch__day__square(
|
|
v-for="(day, dayIndex) in week",
|
|
|
|
:key="dayIndex",
|
|
:transform="getDayPosition(dayIndex)"
|
|
:width="SQUARE_SIZE - SQUARE_BORDER_SIZE",
|
|
:height="SQUARE_SIZE - SQUARE_BORDER_SIZE",
|
|
:style="{ fill: rangeColor[day.colorIndex] }",
|
|
v-tooltip="tooltipOptions(day)",
|
|
@click="$emit('day-click', day)"
|
|
)
|
|
</template>
|
|
|
|
<script>
|
|
// v-if="day.date < now"
|
|
import { formatDate } from '@/utils'
|
|
import { VTooltip } from 'v-tooltip'
|
|
import Heatmap from './Heatmap'
|
|
import { DAYS_IN_WEEK, DEFAULT_LOCALE, DEFAULT_RANGE_COLOR, DEFAULT_TOOLTIP_UNIT, SQUARE_SIZE } from './consts.js'
|
|
|
|
VTooltip.enabled = window.innerWidth > 768
|
|
|
|
export default {
|
|
directives: {
|
|
tooltip: VTooltip
|
|
},
|
|
|
|
props: {
|
|
endDate: { type: String, required: true },
|
|
values: { type: Array, required: true },
|
|
max: { type: Number, default: 10 },
|
|
rangeColor: { type: Array, default: () => DEFAULT_RANGE_COLOR },
|
|
locale: { type: Object, default: () => {} },
|
|
tooltip: { type: Boolean, default: true },
|
|
tooltipUnit: { type: String, default: DEFAULT_TOOLTIP_UNIT },
|
|
vertical: { type: Boolean, default: false },
|
|
noDataText: { type: String, default: null }
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
now: new Date()
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
position() {
|
|
return this.vertical ? 'vertical' : 'horizontal'
|
|
},
|
|
tooltipTransform() {
|
|
return `translate(${this.tooltipX}, ${this.tooltipY})`
|
|
},
|
|
heatmap() {
|
|
return new Heatmap(this.endDate, this.values, this.max)
|
|
},
|
|
width() {
|
|
return {
|
|
horizontal: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE,
|
|
vertical: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.RIGHT_SECTION_WIDTH
|
|
}
|
|
},
|
|
heigth() {
|
|
return {
|
|
horizontal: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.SQUARE_BORDER_SIZE + this.BOTTOM_SECTION_HEIGTH,
|
|
vertical: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE
|
|
}
|
|
},
|
|
viewbox() {
|
|
return `0 0 ${this.width[this.position]} ${this.heigth[this.position]}`
|
|
},
|
|
daysLabelWrapperTransform() {
|
|
return {
|
|
horizontal: `translate(0, ${this.TOP_SECTION_HEIGTH})`,
|
|
vertical: `translate(${this.LEFT_SECTION_WIDTH}, 0)`
|
|
}
|
|
},
|
|
monthsLabelWrapperTransform() {
|
|
return {
|
|
horizontal: `translate(${this.LEFT_SECTION_WIDTH}, 0)`,
|
|
vertical: `translate(0, ${this.TOP_SECTION_HEIGTH})`
|
|
}
|
|
},
|
|
legendWrapperTransform() {
|
|
return {
|
|
horizontal: `translate(${this.width[this.position] - (this.SQUARE_SIZE * this.rangeColor.length) - 30}, ${this.heigth[this.position] - this.BOTTOM_SECTION_HEIGTH})`,
|
|
vertical: `translate(${this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK)}, ${this.TOP_SECTION_HEIGTH})`
|
|
}
|
|
},
|
|
yearWrapperTransform() {
|
|
return `translate(${this.LEFT_SECTION_WIDTH}, ${this.TOP_SECTION_HEIGTH})`
|
|
},
|
|
|
|
SQUARE_BORDER_SIZE: () => SQUARE_SIZE / 5,
|
|
SQUARE_SIZE() { return SQUARE_SIZE + this.SQUARE_BORDER_SIZE },
|
|
TOP_SECTION_HEIGTH() { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
|
|
RIGHT_SECTION_WIDTH() { return this.SQUARE_SIZE * 3 },
|
|
BOTTOM_SECTION_HEIGTH() { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
|
|
LEFT_SECTION_WIDTH() { return Math.ceil(SQUARE_SIZE * 2.5) },
|
|
|
|
lo() {
|
|
if (this.locale) {
|
|
return {
|
|
months: this.locale.months || DEFAULT_LOCALE.months,
|
|
days: this.locale.days || DEFAULT_LOCALE.days,
|
|
on: this.locale.on || DEFAULT_LOCALE.on,
|
|
less: this.locale.less || DEFAULT_LOCALE.less,
|
|
more: this.locale.more || DEFAULT_LOCALE.more
|
|
}
|
|
}
|
|
return DEFAULT_LOCALE
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
tooltipOptions(day) {
|
|
if (this.tooltip) {
|
|
if (day.count != null) {
|
|
return {
|
|
// content: `<b>${day.count} ${this.tooltipUnit}</b> ${this.lo.on} ${this.lo.months[day.date.getMonth()]} ${day.date.getDate()}, ${day.date.getFullYear()}`,
|
|
content: `${formatDate(day.date)} 提取报告,数量:${day.count}`,
|
|
delay: { show: 150, hide: 50 }
|
|
}
|
|
} else if (this.noDataText) {
|
|
return {
|
|
content: `${this.noDataText}`,
|
|
delay: { show: 150, hide: 50 }
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
},
|
|
getWeekPosition(index) {
|
|
if (this.vertical) {
|
|
return `translate(0, ${(this.SQUARE_SIZE * this.heatmap.weekCount) - ((index + 1) * this.SQUARE_SIZE)})`
|
|
}
|
|
return `translate(${index * this.SQUARE_SIZE}, 0)`
|
|
},
|
|
getDayPosition(index) {
|
|
if (this.vertical) {
|
|
return `translate(${index * this.SQUARE_SIZE}, 0)`
|
|
}
|
|
return `translate(0, ${index * this.SQUARE_SIZE})`
|
|
},
|
|
getMonthLabelPostion(month) {
|
|
const position = { x: 0, y: 0 }
|
|
position.x = this.vertical ? 3 : this.SQUARE_SIZE * month.index
|
|
position.y = this.vertical ? (this.SQUARE_SIZE * this.heatmap.weekCount) - (this.SQUARE_SIZE * (month.index)) - (this.SQUARE_SIZE / 4) : this.SQUARE_SIZE - this.SQUARE_BORDER_SIZE
|
|
return position
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
svg.vch__wrapper {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
line-height: 10px;
|
|
}
|
|
|
|
svg.vch__wrapper .vch__months__labels__wrapper text.vch__month__label {
|
|
font-size: 10px;
|
|
}
|
|
|
|
svg.vch__wrapper .vch__days__labels__wrapper text.vch__day__label,
|
|
svg.vch__wrapper .vch__legend__wrapper text {
|
|
font-size: 9px;
|
|
}
|
|
|
|
svg.vch__wrapper .vch__months__labels__wrapper text.vch__month__label,
|
|
svg.vch__wrapper .vch__days__labels__wrapper text.vch__day__label,
|
|
svg.vch__wrapper .vch__legend__wrapper text {
|
|
fill: #767676;
|
|
}
|
|
|
|
svg.vch__wrapper rect.vch__day__square:hover {
|
|
stroke: #555;
|
|
stroke-width: 1px;
|
|
}
|
|
|
|
svg.vch__wrapper rect.vch__day__square:focus {
|
|
outline: none;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.vue-tooltip-theme.tooltip {
|
|
display: block !important;
|
|
z-index: 10000;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip .tooltip-inner {
|
|
background: rgba(0, 0, 0, .7);
|
|
border-radius: 3px;
|
|
color: #ebedf0;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
font-size: 12px;
|
|
line-height: 16px;
|
|
padding: 14px 10px;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip .tooltip-inner b {
|
|
color: white;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip .tooltip-arrow {
|
|
width: 0;
|
|
height: 0;
|
|
border-style: solid;
|
|
position: absolute;
|
|
margin: 5px;
|
|
border-color: black;
|
|
z-index: 1;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="top"] {
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="top"] .tooltip-arrow {
|
|
border-width: 5px 5px 0 5px;
|
|
border-left-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
bottom: -5px;
|
|
left: calc(50% - 5px);
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="bottom"] {
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="bottom"] .tooltip-arrow {
|
|
border-width: 0 5px 5px 5px;
|
|
border-left-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-top-color: transparent !important;
|
|
top: -5px;
|
|
left: calc(50% - 5px);
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="right"] {
|
|
margin-left: 5px;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="right"] .tooltip-arrow {
|
|
border-width: 5px 5px 5px 0;
|
|
border-left-color: transparent !important;
|
|
border-top-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
left: -5px;
|
|
top: calc(50% - 5px);
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="left"] {
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[x-placement^="left"] .tooltip-arrow {
|
|
border-width: 5px 0 5px 5px;
|
|
border-top-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
right: -5px;
|
|
top: calc(50% - 5px);
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[aria-hidden='true'] {
|
|
visibility: hidden;
|
|
opacity: 0;
|
|
transition: opacity .15s, visibility .15s;
|
|
}
|
|
|
|
.vue-tooltip-theme.tooltip[aria-hidden='false'] {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
transition: opacity .15s;
|
|
}
|
|
</style>
|