154 lines
3 KiB
Vue
154 lines
3 KiB
Vue
|
|
<script setup>
|
||
|
|
import { ref, watch } from 'vue'
|
||
|
|
import {
|
||
|
|
Chart as ChartJS,
|
||
|
|
Title,
|
||
|
|
Tooltip,
|
||
|
|
Legend,
|
||
|
|
LineElement,
|
||
|
|
PointElement,
|
||
|
|
CategoryScale,
|
||
|
|
TimeScale,
|
||
|
|
LinearScale,
|
||
|
|
} from 'chart.js'
|
||
|
|
import { Line } from 'vue-chartjs'
|
||
|
|
import 'chartjs-adapter-date-fns'
|
||
|
|
|
||
|
|
ChartJS.register(
|
||
|
|
Title,
|
||
|
|
Tooltip,
|
||
|
|
Legend,
|
||
|
|
LineElement,
|
||
|
|
PointElement,
|
||
|
|
CategoryScale,
|
||
|
|
TimeScale,
|
||
|
|
LinearScale,
|
||
|
|
)
|
||
|
|
|
||
|
|
const props = defineProps({
|
||
|
|
chartsData: {
|
||
|
|
type: Object,
|
||
|
|
required: true,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
|
||
|
|
const originColors = {
|
||
|
|
surugaya: '#FF4560',
|
||
|
|
mandarake: '#775DD0',
|
||
|
|
}
|
||
|
|
|
||
|
|
const chartData = ref({
|
||
|
|
datasets: [],
|
||
|
|
})
|
||
|
|
|
||
|
|
const chartOptions = {
|
||
|
|
responsive: true,
|
||
|
|
maintainAspectRatio: false,
|
||
|
|
animation: false,
|
||
|
|
plugins: {
|
||
|
|
legend: { display: true },
|
||
|
|
tooltip: {
|
||
|
|
mode: 'index',
|
||
|
|
intersect: false,
|
||
|
|
callbacks: {
|
||
|
|
title: (context) => {
|
||
|
|
const date = new Date(context[0].parsed.x)
|
||
|
|
return date.toLocaleString('ru-RU', {
|
||
|
|
year: 'numeric',
|
||
|
|
month: '2-digit',
|
||
|
|
day: '2-digit',
|
||
|
|
hour: '2-digit',
|
||
|
|
minute: '2-digit',
|
||
|
|
})
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
scales: {
|
||
|
|
x: {
|
||
|
|
type: 'time',
|
||
|
|
time: {
|
||
|
|
tooltipFormat: 'dd.MM.yyyy HH:mm',
|
||
|
|
unit: 'day',
|
||
|
|
displayFormats: { day: 'dd MMM' },
|
||
|
|
},
|
||
|
|
title: { display: true, text: 'Date' },
|
||
|
|
},
|
||
|
|
y: {
|
||
|
|
title: { display: true, text: 'Price' },
|
||
|
|
ticks: { precision: 0 },
|
||
|
|
},
|
||
|
|
},
|
||
|
|
elements: {
|
||
|
|
line: { borderWidth: 2, tension: 0, spanGaps: true },
|
||
|
|
point: { radius: 4, hoverRadius: 6 },
|
||
|
|
},
|
||
|
|
interaction: {
|
||
|
|
mode: 'nearest',
|
||
|
|
axis: 'x',
|
||
|
|
intersect: false,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
function transformToChartJSSeries(payload) {
|
||
|
|
const datasets = []
|
||
|
|
for (const origin of payload.origins) {
|
||
|
|
if (!origin.Prices || origin.Prices.length === 0) continue
|
||
|
|
|
||
|
|
const data = origin.Prices.filter((p) => p.value != null)
|
||
|
|
.map((p) => {
|
||
|
|
let y = p.value
|
||
|
|
if (typeof y === 'string') {
|
||
|
|
y = parseFloat(y)
|
||
|
|
}
|
||
|
|
const x =
|
||
|
|
typeof p.created_at === 'number' ? p.created_at * 1000 : new Date(p.created_at).getTime()
|
||
|
|
|
||
|
|
return {
|
||
|
|
x,
|
||
|
|
y: typeof y === 'number' && !isNaN(y) ? y : null,
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.filter((p) => p.y !== null)
|
||
|
|
.sort((a, b) => a.x - b.x)
|
||
|
|
|
||
|
|
if (data.length === 0) continue
|
||
|
|
|
||
|
|
const color = originColors[origin.origin] || '#000000'
|
||
|
|
|
||
|
|
datasets.push({
|
||
|
|
label: origin.origin,
|
||
|
|
data,
|
||
|
|
borderColor: color,
|
||
|
|
backgroundColor: color + '20', // прозрачность
|
||
|
|
fill: false,
|
||
|
|
pointRadius: 4,
|
||
|
|
pointHoverRadius: 6,
|
||
|
|
type: 'line',
|
||
|
|
})
|
||
|
|
}
|
||
|
|
return datasets
|
||
|
|
}
|
||
|
|
|
||
|
|
watch(
|
||
|
|
() => props.chartsData,
|
||
|
|
(newData) => {
|
||
|
|
if (newData?.origins) {
|
||
|
|
const datasets = transformToChartJSSeries(newData)
|
||
|
|
chartData.value = { datasets }
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{ immediate: true },
|
||
|
|
)
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<Line :data="chartData" :options="chartOptions" style="height: 100%; width: 100%" />
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.charts-card {
|
||
|
|
min-width: 80%;
|
||
|
|
}
|
||
|
|
</style>
|