<template> <div> <MkLoading v-if="fetching"/> <div v-show="!fetching" :class="$style.root" class="_panel"> <canvas ref="chartEl"></canvas> </div> </div> </template> <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { Chart } from 'chart.js'; import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import * as os from '@/os'; import 'chartjs-adapter-date-fns'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import gradient from 'chartjs-plugin-gradient'; import { chartVLine } from '@/scripts/chart-vline'; import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; initChart(); const chartEl = $ref<HTMLCanvasElement>(null); const now = new Date(); let chartInstance: Chart = null; const chartLimit = 7; let fetching = $ref(true); const { handler: externalTooltipHandler } = useChartTooltip(); async function renderChart() { if (chartInstance) { chartInstance.destroy(); } const getDate = (ago: number) => { const y = now.getFullYear(); const m = now.getMonth(); const d = now.getDate(); return new Date(y, m, d - ago); }; const format = (arr) => { return arr.map((v, i) => ({ x: getDate(i).getTime(), y: v, })); }; const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; const colorRead = '#3498db'; const colorWrite = '#2ecc71'; const max = Math.max(...raw.read); chartInstance = new Chart(chartEl, { type: 'bar', data: { datasets: [{ parsing: false, label: 'Read', data: format(raw.read).slice().reverse(), pointRadius: 0, borderWidth: 0, borderJoinStyle: 'round', borderRadius: 4, backgroundColor: colorRead, barPercentage: 0.7, categoryPercentage: 0.5, fill: true, }, { parsing: false, label: 'Write', data: format(raw.write).slice().reverse(), pointRadius: 0, borderWidth: 0, borderJoinStyle: 'round', borderRadius: 4, backgroundColor: colorWrite, barPercentage: 0.7, categoryPercentage: 0.5, fill: true, }], }, options: { aspectRatio: 2.5, layout: { padding: { left: 0, right: 8, top: 0, bottom: 0, }, }, scales: { x: { type: 'time', offset: true, time: { stepSize: 1, unit: 'day', }, grid: { display: false, }, ticks: { display: true, maxRotation: 0, autoSkipPadding: 8, }, adapters: { date: { locale: enUS, }, }, }, y: { position: 'left', suggestedMax: 10, grid: { display: true, }, ticks: { display: true, //mirror: true, }, }, }, interaction: { intersect: false, mode: 'index', }, plugins: { legend: { display: false, }, tooltip: { enabled: false, mode: 'index', animation: { duration: 0, }, external: externalTooltipHandler, }, gradient, }, }, plugins: [chartVLine(vLineColor)], }); fetching = false; } onMounted(async () => { renderChart(); }); </script> <style lang="scss" module> .root { padding: 20px; } </style>