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.

182 lines
4.1 KiB

3 weeks ago
<template>
<div ref="chartContainer" class="comparison-bar-chart-container"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as echarts from 'echarts'
// 定义组件属性
interface Props {
chartData?: Array<{
name: string
currentValue: number
previousValue: number
2 weeks ago
growthRate?: number
currentPeriod?: string
previousPeriod?: string
3 weeks ago
}>
title?: string
currentColor?: string
previousColor?: string
showYAxis?: boolean
yAxisName?: string
2 weeks ago
currentLabel?: string
previousLabel?: string
3 weeks ago
}
const props = withDefaults(defineProps<Props>(), {
chartData: () => [],
title: '',
2 weeks ago
currentColor: '#1890FF',
previousColor: '#A9A9A9',
3 weeks ago
showYAxis: true,
2 weeks ago
yAxisName: '数值',
currentLabel: '本期',
previousLabel: '上期'
3 weeks ago
})
// 图表容器引用和实例
const chartContainer = ref<HTMLDivElement | null>(null)
let chartInstance: echarts.ECharts | null = null
2 weeks ago
// 创建图表配置
const createBarOption = (currentLabel: string, previousLabel: string, currentValue: number, previousValue: number) => {
return {
3 weeks ago
tooltip: {
trigger: 'axis',
2 weeks ago
axisPointer: { type: 'shadow' }
3 weeks ago
},
grid: {
2 weeks ago
left: '15%',
right: '10%',
bottom: '20%',
top: '15%'
3 weeks ago
},
xAxis: {
type: 'category',
2 weeks ago
data: [previousLabel, currentLabel],
axisTick: { show: false },
axisLine: { show: false },
3 weeks ago
axisLabel: {
color: 'rgba(255, 255, 255, 0.7)',
2 weeks ago
fontSize: 22
3 weeks ago
}
},
yAxis: {
type: 'value',
2 weeks ago
axisLabel: {
formatter: '{value}',
3 weeks ago
color: 'rgba(255, 255, 255, 0.7)'
},
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.1)'
}
},
2 weeks ago
axisLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
3 weeks ago
}
},
2 weeks ago
series: [{
name: '用量',
type: 'bar',
barWidth: '60%',
data: [
{ value: previousValue, itemStyle: { color: props.previousColor } },
{ value: currentValue, itemStyle: { color: props.currentColor } }
],
label: {
show: true,
position: 'top',
fontWeight: 'bold',
color: '#fff',
formatter: (params: any) => {
return params.value > 0 ? params.value.toFixed(0) : '';
3 weeks ago
}
}
2 weeks ago
}],
// 添加标题显示增长率
title: props.chartData[0]?.growthRate !== undefined ? {
text: `${props.chartData[0].growthRate >= 0 ? '↑' : '↓'} ${(props.chartData[0].growthRate * 100).toFixed(1)}%`,
left: 'center',
top: '5%',
textStyle: {
fontSize: 32,
fontWeight: 'bold',
color: props.chartData[0].growthRate >= 0 ? '#52c41a' : '#f5222d',
fontFamily: 'Arial, sans-serif'
}
} : undefined
2 weeks ago
};
}
// 初始化图表
const initChart = () => {
if (chartContainer.value) {
chartInstance = echarts.init(chartContainer.value)
updateChart()
3 weeks ago
}
2 weeks ago
}
// 更新图表
const updateChart = () => {
if (!chartInstance || !props.chartData.length) return
const dataItem = props.chartData[0]
// 优先使用period字段作为标签,如果没有则使用默认标签
const currentLabel = dataItem.currentPeriod || props.currentLabel
const previousLabel = dataItem.previousPeriod || props.previousLabel
const option = createBarOption(
currentLabel,
previousLabel,
dataItem.currentValue,
dataItem.previousValue
)
3 weeks ago
chartInstance.setOption(option)
}
// 监听数据变化
watch(
() => props.chartData,
() => {
updateChart()
},
{ deep: true }
)
// 窗口大小改变时重绘图表
const handleResize = () => {
if (chartInstance) {
chartInstance.resize()
}
}
// 组件挂载时初始化图表
onMounted(() => {
3 weeks ago
initChart()
3 weeks ago
window.addEventListener('resize', handleResize)
})
// 组件销毁前清理资源
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
if (chartInstance) {
chartInstance.dispose()
chartInstance = null
}
})
</script>
<style scoped>
.comparison-bar-chart-container {
width: 100%;
height: 100%;
}
</style>