139 vuetify, vuedraggable, + @ IE11 이슈, chart.js 3.6.4, chartjs-plugin-zoom 1.2.0
source: categories/study/vue-experiance/vue-experiance_9-99_40.md
139 vuetify, vuedraggable, + @ IE11 이슈, chart.js 3.6.4, chartjs-plugin-zoom 1.2.0
전역 컴포넌트 등록으로 해결..!!
- 지역 컴포넌트로 불러왔는데 순환참조 이슈 발생
- 여러 테스트를 거쳤으나 정확한 원인 파악 어려움
- 혹시 역으로 전역으로 등록해서 네임스페이스를 확고히 해야되나? 라는 생각을 함
- 근거는 없지만 그냥 이런 생각을 함..
- 하도 해결방법을 못찾겟어서..
- 그런데 이 방법으로 해결이됨..
Vue.component('DraggableComponent', draggable);
chart.js 3.6.4, chartjs-plugin-zoom 1.2.0
PartStatusTransitPopupDetailCollectedDataGraph.vue
<template>
<div class="canvas_area">
<canvas id="myChart" ref="chartCanvas" width="400" height="400"></canvas>
</div>
</template>
<script>
import { Chart, registerables } from 'chart.js';
import { mapGetters } from 'vuex';
Chart.register(...registerables);
export default {
name: 'PartStatusTransitPopupDetailCollectedDataGraph',
props: {
noticeLimit: {
type: Object,
},
},
data() {
return {
myChart: null,
graphData: [],
chartData: {
type: 'line',
data: {
labels: [],
datasets: [
{
label: '수집데이터',
data: [],
xAxisID: 'xAxes',
backgroundColor: '#FF2F00',
borderColor: '#FF7F00',
borderWidth: 1.5,
strokeColor: '#FF7F00',
pointBackgroundColor: [],
pointRadius: [],
fill: false,
fillColor: '#FF7F00',
},
],
},
options: {
interaction: {
intersect: false,
},
scales: {
xAxes: {
ticks: {
callback: {},
maxTicksLimit: 10,
},
},
},
plugins: {
legend: {
display: true,
},
tooltip: {
enabled: false,
position: 'nearest',
external: this.external,
},
},
},
},
};
},
computed: {
...mapGetters('transit', ['boardData', 'pubReportData']),
},
watch: {
// 수집데이터
boardData(val) {
this.initChart(val);
},
pubReportData(val) {
this.initChart(val);
},
},
beforeDestroy() {
// 차트 정보 삭제
if (this.myChart) {
this.myChart.destroy();
}
},
methods: {
initChart(val) {
this.graphData = [...val];
this.chartData.data.datasets[0].data = val.map(e => e.value);
this.chartData.data.labels = val.map(e => e.collectTime);
// 라벨 콜백 적용
this.convertXLabelCallback(this.chartData.data.labels);
// 이상 알림발생 포인트 적용
this.pointAbnormalNoticeHistory(this.chartData.data.datasets[0]);
// 임계치 설정
this.limitBackground();
this.createChart(this.$refs.chartCanvas, this.chartData);
},
createChart(ref, chartData) {
const ctx = ref.getContext('2d');
if (this.myChart) {
this.myChart.destroy();
}
this.myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
},
external(context) {
// Tooltip Element
let tooltipEl = document.getElementById('chartjs-tooltip');
// 이상알림 발생된 이력만 표시
const tooltipItems = context.tooltip.$context
? context.tooltip.$context.tooltipItems
: [];
if (tooltipItems.length < 1) {
if (tooltipEl) {
tooltipEl.remove();
}
return;
}
const dataIndex = tooltipItems[0].dataIndex;
if (!this.graphData[dataIndex].history) {
if (tooltipEl) {
tooltipEl.remove();
}
return;
}
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<div class="tooltip_area"></div>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
const tooltipModel = context.tooltip;
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
tooltipEl.style.transition = 'opacity .3s';
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
// Set Text
if (tooltipModel.body) {
const {
sensorTypeName,
history: {
sensorName,
noticeDivisionName,
limitValueName,
measureValueName,
measureTime,
content,
location,
},
} = this.graphData[dataIndex];
const innerHtml = `<div>
<dl>
<div class="definition_item">
<dt class="tit">센서타입</dt>
<dd class="txt">${sensorTypeName || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">센서명</dt>
<dd class="txt">${sensorName || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">알림구분</dt>
<dd class="txt">${noticeDivisionName || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">임계치</dt>
<dd class="txt">${limitValueName || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">측정값</dt>
<dd class="txt ${measureValueName && 'is_red'}">${
measureValueName || '-'
}</dd>
</div>
<div class="definition_item">
<dt class="tit">발생일시</dt>
<dd class="txt">${measureTime || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">알림내용</dt>
<dd class="txt">${content || '-'}</dd>
</div>
<div class="definition_item">
<dt class="tit">발생위치</dt>
<dd class="txt">${location || '-'}</dd>
</div>
</dl>
</div>`;
const tableRoot = tooltipEl.querySelector('.tooltip_area');
tableRoot.innerHTML = innerHtml;
}
const targetCanvas = context.chart.canvas;
const position = targetCanvas.getBoundingClientRect();
const positionLeft = position.left + scrollX + tooltipModel.caretX;
tooltipEl.style.opacity = 1;
tooltipEl.style.position = 'absolute';
tooltipEl.style.zIndex = 200;
tooltipEl.style.left = positionLeft + 'px';
tooltipEl.style.top = position.top + scrollY + tooltipModel.caretY + 'px';
tooltipEl.style.pointerEvents = 'none';
},
// 지정 라벨만 적용 (스코프)
convertXLabelCallback(collectTimeList) {
const group = {};
const groupIndex = [];
const xLabels = collectTimeList.map((e, index) => {
const date = new Date(e);
const label = `${date.getDate()}일${date.getHours()}시`;
if (!group[label]) {
group[label] = true;
groupIndex.push(index);
}
return label;
});
// 지정 라벨만 적용 (스코프)
this.chartData.options.scales.xAxes.ticks.callback = (_, index) => {
if (groupIndex.includes(index)) {
return xLabels[index];
}
};
},
// 이상 알림 발생구간만 포인트
pointAbnormalNoticeHistory(dataset) {
this.graphData.forEach((e, index) => {
// 기본값
dataset.pointBackgroundColor[index] = 'orange';
if (e.history) {
dataset.pointBackgroundColor[index] = 'red';
dataset.pointRadius[index] = 3;
} else {
dataset.pointRadius[index] = 0;
}
});
},
// 임계치 백그라운드 설정
limitBackground() {
const graphDataLength = this.graphData.length;
const upperLimit = [];
const lowerLimit = [];
for (let i = 0; i < graphDataLength; ++i) {
upperLimit.push(this.noticeLimit.upperValue);
lowerLimit.push(this.noticeLimit.lowerValue);
}
// 초기화
this.chartData.data.datasets = [{ ...this.chartData.data.datasets[0] }];
if (this.noticeLimit.upperValue) {
this.chartData.data.datasets.push({
label: '상한값',
data: upperLimit,
borderWidth: 1,
borderColor: '#FFFFE0',
backgroundColor: 'rgba(255,255,224,0.7)',
fill: this.noticeLimit.lowerValue ? -1 : 'start',
});
}
if (this.noticeLimit.lowerValue) {
this.chartData.data.datasets.push({
label: '하한값',
data: lowerLimit,
borderWidth: 1,
borderColor: '#FFFFE0',
backgroundColor: 'rgba(255,255,224,0.7)',
fill: this.noticeLimit.upperValue ? +1 : 'end',
});
}
},
},
};
</script>
<style scoped lang="scss"></style>
DeliveryLineChart.vue
<template>
<div class="canvas_area">
<canvas id="myChart" ref="lineChart" width="800" height="800"></canvas>
</div>
</template>
<script>
import { Chart, registerables } from 'chart.js';
import { mapGetters } from 'vuex';
// 임계치 헬퍼 플러그인
const helperPlugin = {
id: 'limitHelperPlugin',
afterDraw: chart => {
const context = chart.ctx;
const limitConfig = chart.config.data.limitConfig;
const limitStrokeDraw = (index, config, valueName) => {
const value = config[valueName];
const filteringLegendItem = chart.legend.legendItems.filter(
legendItem => legendItem.datasetIndex === Number(index),
)[0];
const filteringInSuggestedMax =
chart.options.scales.yAxes.suggestedMax >= value;
const filteringInSuggestedMin =
chart.options.scales.yAxes.suggestedMin <= value;
if (
value &&
!filteringLegendItem.hidden &&
filteringInSuggestedMax &&
filteringInSuggestedMin
) {
const xAxis = chart.scales.x; // 기본 x 축
const yAxis = chart.scales.yAxes;
context.save();
context.beginPath();
context.moveTo(xAxis.left, yAxis.getPixelForValue(value));
context.strokeStyle = config.color;
context.setLineDash([8, 6]);
context.lineTo(xAxis.right, yAxis.getPixelForValue(value));
context.stroke();
}
};
if (limitConfig && Array.isArray(limitConfig)) {
for (const index in limitConfig) {
const config = limitConfig[index];
limitStrokeDraw(index, config, 'upperValue');
limitStrokeDraw(index, config, 'lowerValue');
}
}
context.restore();
},
};
Chart.register(...registerables, helperPlugin);
export default {
name: 'DeliveryLineChart',
data() {
return {
myChart: null,
chartData: {
type: 'line',
data: {
labels: [],
datasets: [
{
label: '',
backgroundColor: 'transparent',
pointBackgroundColor: '#fff',
borderWidth: 2,
borderColor: '#4E76DE',
pointBorderColor: '#4E76DE',
data: [],
fill: true, // true 일 경우 색상이 채워진다.
lineTension: 0.4, // 곡선 라인
radius: 0.1,
},
],
},
options: {
grid: {
display: true,
},
interaction: {
intersect: false,
},
scales: {
yAxes: {
beginAtZero: true,
suggestedMax: 60,
suggestedMin: -40,
padding: 10,
},
},
plugins: {
legend: {
display: true,
align: 'end',
textDirection: 'ltr',
labels: {
boxWidth: 12,
boxHeight: 12,
padding: 24,
},
},
tooltip: {
enabled: true,
position: 'nearest',
intersect: false,
alignment: 'center',
custom(tooltip) {
if (!tooltip) {
return;
}
tooltip.displayColors = false;
},
callbacks: {
title(tooltipItem) {
const { label } = tooltipItem[0];
return label;
},
},
},
},
},
},
sensor: {
TEMP: {
color: '#4E76DE',
label: '온도',
},
HUMD: {
color: '#E16E6E',
label: '습도',
},
COLL: {
color: '#9B71F7',
label: '충격',
},
BATT: {
color: '#29A7B3',
label: '배터리',
},
},
};
},
computed: {
...mapGetters('delivery', ['deliveryChartData']),
},
watch: {
deliveryChartData() {
this.setChartData();
},
},
beforeDestroy() {
// 차트 정보 삭제
if (this.myChart) {
this.myChart.destroy();
}
},
mounted() {},
methods: {
initChart(ref, chartData) {
// 컨텍스트
const ctx = ref.getContext('2d');
if (this.myChart) {
this.myChart.destroy();
}
this.myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
},
setChartData() {
const getRandomColorWithoutRed = () => {
let color = '#';
const randomHex = '0123456789ABCDEF';
while (color.length <= 6) {
color += randomHex[Math.floor(Math.random() * 16)] || '';
}
return color;
};
const graphColorList = [
'#000080',
'#50BCDF',
'#FFFF00',
'#FF0000',
'#008000',
'#81C147',
'#8B00FF',
'#660099',
'#873904',
'#FF7F00',
'#FFC0CB',
];
const objChart = {
label: '온도',
backgroundColor: 'transparent',
pointBackgroundColor: '#fff',
borderWidth: 2,
borderColor: '#4E76DE',
pointBorderColor: '#4E76DE',
data: [],
fill: true, // true 일 경우 색상이 채워진다.
lineTension: 0.4, // 곡선 라인
radius: 0.1,
};
const chartData = [];
const limitConfig = [];
const suggestedMax = Math.max.apply(
null,
this.deliveryChartData.deviceCodeList
.map(item => item.data)
.map(item =>
item.reduce((prev, curr) =>
Number(prev) > Number(curr) ? Number(prev) : Number(curr),
),
),
);
const suggestedMin = Math.min.apply(
null,
this.deliveryChartData.deviceCodeList
.map(item => item.data)
.map(item =>
item.reduce((prev, curr) =>
Number(prev) < Number(curr) ? Number(prev) : Number(curr),
),
),
);
const optionalCategoryName = categoryName => {
if (categoryName) {
return '(' + categoryName + ')';
}
return '';
};
this.chartData.data.labels = this.deliveryChartData.sensorLabels;
const applyColors = [];
for (const index in this.deliveryChartData.deviceCodeList) {
const item = this.deliveryChartData.deviceCodeList[index];
let color = graphColorList[index] || getRandomColorWithoutRed();
// let color = getRandomColorWithoutRed();
while (applyColors.includes(color)) {
color = getRandomColorWithoutRed();
}
const dataObj = {
...objChart,
label:
this.sensor[item.sensorType].label +
optionalCategoryName(item.categoryName),
borderColor: color,
pointBorderColor: color,
data: item.data,
};
const configObj = {
lowerValue: item.lowerValue,
color,
upperValue: item.upperValue,
};
chartData.push(dataObj);
limitConfig.push(configObj);
applyColors.push(color);
}
// console.log('chartData', chartData);
this.chartData.data.datasets = chartData;
this.chartData.data.limitConfig = limitConfig;
// 그래프 최소값, 최대값 유동적으로 가져가기
const offsetSuggestedMax = 0;
const offsetSuggestedMin = 0;
this.chartData.options.scales.yAxes.suggestedMax = Math.max(
this.chartData.options.scales.yAxes.suggestedMax,
(suggestedMax || 0) + offsetSuggestedMax,
);
this.chartData.options.scales.yAxes.suggestedMin = Math.min(
this.chartData.options.scales.yAxes.suggestedMin,
(suggestedMin || 0) - offsetSuggestedMin,
);
this.initChart(this.$refs.lineChart, this.chartData);
},
},
};
</script>
DeviceLineChart.vue
<template>
<div class="canvas_area">
<canvas
id="myChart"
ref="lineChart"
class="canvas"
width="400"
height="100"
></canvas>
<span class="tooltip">
확대: 그래프 영역 클릭 후 마우스휠<br />
x축 이동: 그래프 영역 마우스 드래그<br />
그래프 영역 재클릭: 확대 기능 off
</span>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { Chart, registerables } from 'chart.js';
import zoomPlugin from 'chartjs-plugin-zoom';
import moment from '@/plugins/moment';
Chart.register(...registerables);
Chart.register(zoomPlugin);
export default {
name: 'DeviceChartLine',
props: {
period: {
type: Number,
required: true,
},
periodDt: {
type: Object,
required: true,
validator(obj) {
return (
'startDt' in obj &&
typeof obj.startDt === 'string' &&
'endDt' in obj &&
typeof obj.endDt === 'string'
);
},
},
},
data() {
return {
myChart: null,
chartData: {
type: 'line',
data: {
labels: [],
datasets: [
// period 48시간 이내
// {
// label: '데이터값',
// backgroundColor: 'rgba(78,118,222,0.3)',
// pointBackgroundColor: '#fff',
// borderWidth: 2,
// borderColor: '#4E76DE',
// pointBorderColor: '#4E76DE',
// data: [40, 85, 90, 20, 60],
// fill: true, // true 일 경우 색상이 채워진다.
// lineTension: 0.4, // 곡선 라인
// pointStyle: ['circle', 'circle', 'crossRot', 'circle', 'circle'],
// },
{
label: '일 평균',
backgroundColor: 'rgba(78,118,222,0.3)',
pointBackgroundColor: '#fff',
borderWidth: 2,
borderColor: '#4E76DE',
pointBorderColor: '#4E76DE',
data: [40, 85, 90, 20, 60],
fill: true, // true 일 경우 색상이 채워진다.
lineTension: 0.4, // 곡선 라인
pointStyle: ['circle', 'circle', 'crossRot', 'circle', 'circle'],
},
{
label: '최소값',
backgroundColor: 'transparent',
borderWidth: 1,
borderDash: [5, 5],
borderColor: '#000',
pointBorderColor: '#000',
data: [10, 10, 10, 10, 10],
pointBackgroundColor: '#fff',
fill: false,
pointRadius: 0,
pointHitRadius: 5,
},
{
label: '최대값',
backgroundColor: 'rgba(78,118,222,0.3)',
pointBackgroundColor: '#fff',
borderWidth: 1,
borderColor: '#C0502A',
pointBorderColor: '#C0502A',
data: [10, 10, 10, 10, 10],
fill: false,
lineTension: 0.4,
},
],
},
options: {
grid: {
display: true,
},
interaction: {
intersect: false,
},
scales: {
y: {
// display: true,
// suggestedMin: 0,
// suggestedMax: 100,
ticks: {
stepSize: 1,
callback(val, index, ticks) {
const range = Math.round(
(ticks[ticks.length - 1].value - ticks[0].value) / 4,
);
if (index === 0) {
return val;
}
if (ticks[index].value % range === 0) {
return val;
}
// 확대시 y 축 겹침 방지 조건문
if (
index === ticks.length - 1 &&
ticks[index].value % range > 5
) {
return val;
}
},
},
},
},
plugins: {
legend: {
display: true,
},
tooltip: {
enabled: true,
position: 'nearest',
intersect: false,
alignment: 'center',
custom(tooltip) {
if (!tooltip) {
return;
}
tooltip.displayColors = false;
},
callbacks: {
title(tooltipItem) {
const { label } = tooltipItem[0];
// const { datasetIndex, label } = tooltipItem[0];
// return datasetIndex === 0 ? label : '평균';
return label;
},
},
},
zoom: {
pan: {
enabled: true,
mode: 'x',
},
zoom: {
mode: 'x',
wheel: {
enabled: false,
},
pinch: {
enabled: false,
},
},
},
},
onClick(e) {
// 차트 클릭 이벤트 등록
const chart = e.chart;
chart.options.plugins.zoom.zoom.wheel.enabled =
!chart.options.plugins.zoom.zoom.wheel.enabled;
chart.options.plugins.zoom.zoom.pinch.enabled =
!chart.options.plugins.zoom.zoom.pinch.enabled;
chart.update();
},
},
plugins: [
// 클릭했을 때 차트 테두리 설정
{
id: 'chartAreaBorder',
beforeDraw(chart) {
const {
ctx,
chartArea: { left, top, width, height },
} = chart;
if (chart.options.plugins.zoom.zoom.wheel.enabled) {
ctx.save();
ctx.strokeStyle = 'blue';
ctx.lineWidth = 1;
ctx.strokeRect(left, top, width, height);
ctx.restore();
}
},
},
],
},
// 측정값 또는 일평균
dataSetType1: {
label: '',
backgroundColor: 'rgba(78,118,222,0.3)',
pointBackgroundColor: '#fff',
borderWidth: 2,
// borderColor: 'transparent',
borderColor: '#4E76DE',
// pointBorderColor: 'transparent',
pointBorderColor: '#4E76DE',
data: [],
fill: true, // true 일 경우 색상이 채워진다.
stepped: true,
tension: 0.1,
pointRadius: 0.1,
pointHoverRadius: 0.1,
},
// 최소값
dataSetType2: {
label: '',
backgroundColor: 'transparent',
borderWidth: 1,
borderDash: [5, 5],
borderColor: '#000',
pointBorderColor: '#000',
data: [],
pointBackgroundColor: '#fff',
fill: false,
stepped: true,
tension: 0.1,
pointRadius: 0,
pointHoverRadius: 0,
},
// 최대값
dataSetType3: {
label: '',
backgroundColor: 'rgba(78,118,222,0.3)',
pointBackgroundColor: '#fff',
borderWidth: 1,
borderColor: '#C0502A',
pointBorderColor: '#C0502A',
data: [],
fill: false,
stepped: true,
tension: 0.1,
pointRadius: 0,
pointHoverRadius: 0,
},
// 검색 기간이 2일 이하일 때 생성하는 배열
lessThanTwoDaysData: {},
};
},
computed: {
...mapGetters('info', [
'deviceChartDataList',
'deviceChartMaxDataList',
'deviceChartMinDataList',
]),
},
watch: {
deviceChartDataList() {
this.setChartData();
this.initChart(this.$refs.lineChart, this.chartData);
},
},
beforeDestroy() {
// 차트 정보 삭제
if (this.myChart) {
this.myChart.destroy();
}
},
mounted() {
this.setChartData();
this.initChart(this.$refs.lineChart, this.chartData);
},
methods: {
initChart(ref, chartData) {
const ctx = ref.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(78,118,222,0.3)');
gradient.addColorStop(1, 'rgba(78,118,222,0.3)');
this.chartData.data.datasets[0].backgroundColor = gradient;
if (this.myChart) {
this.myChart.destroy();
}
this.myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
plugins: chartData.plugins,
});
},
initData() {
if (this.period === 0) {
this.lessThanTwoDaysData = {};
// 검색 기간이 하루일 때, 1분마다 데이터 1개씩, 1 * 60 * 24
Array(60 * 24)
.fill()
.forEach((_, i) => {
const h =
Math.floor(i / 60) < 10
? `0${Math.floor(i / 60)}`
: Math.floor(i / 60);
const m = i % 60 < 10 ? `0${i % 60}` : i % 60;
this.$set(
this.lessThanTwoDaysData,
`${this.periodDt.startDt} ${h}:${m}`,
NaN,
);
});
} else if (this.period === 1) {
this.lessThanTwoDaysData = {};
// 검색 기간이 2틀일 때,
Array(2 * 60 * 24)
.fill()
.forEach((_, i) => {
let h =
Math.floor(i / 60) < 10
? `0${Math.floor(i / 60)}`
: Math.floor(i / 60);
h = h < 24 ? h : h - 24 < 10 ? `0${h - 24}` : h - 24;
const m = i % 60 < 10 ? `0${i % 60}` : i % 60;
if (i < 1440) {
this.$set(
this.lessThanTwoDaysData,
`${this.periodDt.startDt} ${h}:${m}`,
NaN,
);
} else {
this.$set(
this.lessThanTwoDaysData,
`${this.periodDt.endDt} ${h}:${m}`,
NaN,
);
}
});
}
},
setChartData() {
this.initData();
if (this.period < 2) {
this.deviceChartDataList.forEach(v => {
this.lessThanTwoDaysData[v.name] = v.value;
});
const sortObj = Object.entries(this.lessThanTwoDaysData).sort();
const keys = sortObj.map(v => v[0]);
const dataList = sortObj.map(v => v[1]);
const pointStyle = dataList.map(item =>
!isNaN(item) ? 'circle' : 'crossRot',
);
this.chartData.data.labels = keys.map(item => {
return moment.getConvertFormat(item, 'MM/DD HH:mm');
});
this.$set(this.chartData.data, 'datasets', [
{
...this.dataSetType1,
label: '측정값',
pointStyle,
data: dataList,
},
]);
} else {
this.deviceChartDataList.forEach(v => {
this.lessThanTwoDaysData[v.name] = v.value;
});
const keys = this.deviceChartDataList.map(item => item.name);
const values = this.deviceChartDataList.map(item => item.value);
const dataList = values.map(item => (item !== '' ? item : 0));
const maxValues = this.deviceChartMaxDataList.map(item => item.value);
const maxDataList = maxValues.map(item => (item !== '' ? item : 0));
const minValues = this.deviceChartMinDataList.map(item => item.value);
const minDataList = minValues.map(item => (item !== '' ? item : 0));
const pointStyle = values.map(item =>
item !== '' ? 'circle' : 'crossRot',
);
this.chartData.data.labels = keys.map(item =>
moment.getConvertFormat(item, 'MM/DD'),
);
this.$set(this.chartData.data, 'datasets', [
{
...this.dataSetType1,
label: '일 평균',
pointStyle,
data: dataList,
},
{
...this.dataSetType2,
label: '최소값',
data: minDataList,
},
{
...this.dataSetType3,
label: '최대값',
data: maxDataList,
},
]);
}
},
},
};
</script>
<style scoped lang="scss">
.canvas {
cursor: pointer;
}
.canvas_area {
position: relative;
.tooltip {
display: none;
position: absolute;
top: 0;
left: 250px;
padding: 10px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.7);
font-size: 14px;
color: #fff;
transform: translateY(-100%);
&:before {
position: absolute;
bottom: -8px;
left: 4px;
width: 0;
border-top: 8px solid rgba(0, 0, 0, 0.7);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
content: '';
}
}
&:hover {
.tooltip {
display: block;
}
}
}
</style>
chart.js 2.9.4 다운그레이드
chartjs-plugin-zoom 0.7.7 다운그레이드
그래야 IE11 지원 가능