115 lines
2.6 KiB
Vue
115 lines
2.6 KiB
Vue
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
|
||
import type { ReportTrendPoint } from '../types'
|
||
|
||
const props = defineProps<{
|
||
points: ReportTrendPoint[]
|
||
}>()
|
||
|
||
const chartBars = computed(() => {
|
||
const width = 640
|
||
const height = 200
|
||
const paddingX = 30
|
||
const paddingY = 30
|
||
const slotWidth = (width - paddingX * 2) / Math.max(props.points.length, 1)
|
||
const barWidth = slotWidth * 0.55
|
||
const maxValue = Math.max(...props.points.map((point) => point.value), 3) * 1.2
|
||
|
||
return props.points.map((point, index) => {
|
||
const barHeight = (point.value / maxValue) * (height - paddingY * 2)
|
||
const x = paddingX + slotWidth * index + (slotWidth - barWidth) / 2
|
||
const y = height - paddingY - barHeight
|
||
|
||
return {
|
||
...point,
|
||
x,
|
||
y,
|
||
width: barWidth,
|
||
height: Math.max(barHeight, 2),
|
||
}
|
||
})
|
||
})
|
||
|
||
const gridLines = [0, 1, 2, 3].map((index) => 30 + ((200 - 60) / 3) * index)
|
||
</script>
|
||
|
||
<template>
|
||
<div v-if="points.length" class="chart-wrap">
|
||
<svg viewBox="0 0 640 200" role="img" aria-label="每日签署量趋势柱状图">
|
||
<defs>
|
||
<linearGradient id="report-bar-gradient" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0" stop-color="#18a0c0" />
|
||
<stop offset="1" stop-color="#0e6e8c" />
|
||
</linearGradient>
|
||
</defs>
|
||
|
||
<line
|
||
v-for="line in gridLines"
|
||
:key="line"
|
||
x1="30"
|
||
:y1="line"
|
||
x2="610"
|
||
:y2="line"
|
||
stroke="#e6eef2"
|
||
stroke-dasharray="3 4"
|
||
/>
|
||
|
||
<g v-for="bar in chartBars" :key="bar.date">
|
||
<rect
|
||
:x="bar.x"
|
||
:y="bar.y"
|
||
:width="bar.width"
|
||
:height="bar.height"
|
||
rx="4"
|
||
:fill="bar.value ? 'url(#report-bar-gradient)' : '#e2ebef'"
|
||
>
|
||
<title>{{ bar.label }}:{{ bar.value }} 单</title>
|
||
</rect>
|
||
<text
|
||
v-if="bar.value"
|
||
:x="bar.x + bar.width / 2"
|
||
:y="bar.y - 6"
|
||
text-anchor="middle"
|
||
font-size="10.5"
|
||
fill="#0a4f66"
|
||
font-weight="bold"
|
||
>
|
||
{{ bar.value }}
|
||
</text>
|
||
<text
|
||
:x="bar.x + bar.width / 2"
|
||
y="190"
|
||
text-anchor="middle"
|
||
font-size="9.5"
|
||
fill="#687f8b"
|
||
>
|
||
{{ bar.label }}
|
||
</text>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div v-else class="empty-chart">暂无趋势数据</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.chart-wrap {
|
||
width: 100%;
|
||
min-height: 200px;
|
||
}
|
||
|
||
.chart-wrap svg {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.empty-chart {
|
||
display: grid;
|
||
min-height: 200px;
|
||
color: var(--mut);
|
||
font-size: 13px;
|
||
place-items: center;
|
||
}
|
||
</style>
|