Skip to content

VbenChart 图表组件

基于 ECharts 的图表组件,支持多种图表类型和响应式布局。

基础用法

vue
<script setup lang="ts">
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const option: EChartsOption = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [150, 230, 224, 218, 135, 147, 260],
      type: 'line'
    }
  ]
}
</script>

<template>
  <VbenChart :option="option" height="400px" />
</template>

柱状图

vue
<script setup lang="ts">
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const option: EChartsOption = {
  title: {
    text: '月度销售额'
  },
  tooltip: {
    trigger: 'axis'
  },
  xAxis: {
    type: 'category',
    data: ['1月', '2月', '3月', '4月', '5月', '6月']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      name: '销售额',
      type: 'bar',
      data: [120, 200, 150, 80, 70, 110],
      itemStyle: {
        color: '#1890ff'
      }
    }
  ]
}
</script>

<template>
  <VbenChart :option="option" height="400px" />
</template>

饼图

vue
<script setup lang="ts">
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const option: EChartsOption = {
  title: {
    text: '用户来源',
    left: 'center'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b}: {c} ({d}%)'
  },
  legend: {
    orient: 'vertical',
    left: 'left'
  },
  series: [
    {
      name: '访问来源',
      type: 'pie',
      radius: '50%',
      data: [
        { value: 1048, name: '搜索引擎' },
        { value: 735, name: '直接访问' },
        { value: 580, name: '邮件营销' },
        { value: 484, name: '联盟广告' },
        { value: 300, name: '视频广告' }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ]
}
</script>

<template>
  <VbenChart :option="option" height="400px" />
</template>

折线图

vue
<script setup lang="ts">
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const option: EChartsOption = {
  title: {
    text: '温度变化'
  },
  tooltip: {
    trigger: 'axis'
  },
  legend: {
    data: ['最高气温', '最低气温']
  },
  xAxis: {
    type: 'category',
    boundaryGap: false,
    data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
  },
  yAxis: {
    type: 'value',
    axisLabel: {
      formatter: '{value} °C'
    }
  },
  series: [
    {
      name: '最高气温',
      type: 'line',
      data: [11, 11, 15, 13, 12, 13, 10],
      smooth: true
    },
    {
      name: '最低气温',
      type: 'line',
      data: [1, -2, 2, 5, 3, 2, 0],
      smooth: true
    }
  ]
}
</script>

<template>
  <VbenChart :option="option" height="400px" />
</template>

API

Props

参数说明类型默认值
optionECharts 配置项EChartsOption-
height图表高度string | number'400px'
width图表宽度string | number'100%'
theme主题string | object-
loading是否显示加载动画booleanfalse
loadingOption加载动画配置object-
autoResize是否自动调整大小booleantrue

Events

事件名说明回调参数
click点击事件(params: any) => void
dblclick双击事件(params: any) => void
mouseover鼠标移入事件(params: any) => void
mouseout鼠标移出事件(params: any) => void
legendselectchanged图例选择变化(params: any) => void

Methods

方法名说明参数
setOption设置图表配置(option: EChartsOption, notMerge?: boolean) => void
resize调整图表大小() => void
clear清空图表() => void
dispose销毁图表实例() => void
getDataURL获取图表图片() => string

完整示例

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const chartRef = ref()
const loading = ref(true)

const option = ref<EChartsOption>({
  title: {
    text: '销售数据统计',
    left: 'center'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross'
    }
  },
  legend: {
    data: ['销售额', '利润'],
    top: 30
  },
  grid: {
    left: '3%',
    right: '4%',
    bottom: '3%',
    containLabel: true
  },
  xAxis: {
    type: 'category',
    boundaryGap: false,
    data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      name: '销售额',
      type: 'line',
      stack: 'Total',
      smooth: true,
      lineStyle: {
        width: 0
      },
      showSymbol: false,
      areaStyle: {
        opacity: 0.8,
        color: {
          type: 'linear',
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            { offset: 0, color: 'rgba(24, 144, 255, 0.8)' },
            { offset: 1, color: 'rgba(24, 144, 255, 0.1)' }
          ]
        }
      },
      emphasis: {
        focus: 'series'
      },
      data: [140, 232, 101, 264, 90, 340, 250, 180, 200, 220, 180, 240]
    },
    {
      name: '利润',
      type: 'line',
      stack: 'Total',
      smooth: true,
      lineStyle: {
        width: 0
      },
      showSymbol: false,
      areaStyle: {
        opacity: 0.8,
        color: {
          type: 'linear',
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            { offset: 0, color: 'rgba(82, 196, 26, 0.8)' },
            { offset: 1, color: 'rgba(82, 196, 26, 0.1)' }
          ]
        }
      },
      emphasis: {
        focus: 'series'
      },
      data: [120, 182, 191, 234, 290, 330, 310, 201, 154, 190, 330, 410]
    }
  ]
})

onMounted(async () => {
  // 模拟数据加载
  await new Promise(resolve => setTimeout(resolve, 1000))
  loading.value = false
})

const handleClick = (params: any) => {
  console.log('点击:', params)
}

const handleDownload = () => {
  const url = chartRef.value?.getDataURL()
  const link = document.createElement('a')
  link.href = url
  link.download = 'chart.png'
  link.click()
}

const handleRefresh = async () => {
  loading.value = true
  // 重新加载数据
  await new Promise(resolve => setTimeout(resolve, 1000))
  loading.value = false
}
</script>

<template>
  <div class="chart-container">
    <div class="chart-header">
      <h3>销售数据统计</h3>
      <a-space>
        <a-button @click="handleRefresh">刷新</a-button>
        <a-button @click="handleDownload">下载</a-button>
      </a-space>
    </div>
    
    <VbenChart
      ref="chartRef"
      :option="option"
      :loading="loading"
      height="500px"
      @click="handleClick"
    />
  </div>
</template>

<style scoped>
.chart-container {
  padding: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.chart-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.chart-header h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}
</style>

响应式图表

vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import { VbenChart } from '@vben/common-ui'
import { useWindowSize } from '@vueuse/core'
import type { EChartsOption } from 'echarts'

const { width } = useWindowSize()

const option = computed<EChartsOption>(() => ({
  title: {
    text: '响应式图表',
    left: 'center',
    textStyle: {
      fontSize: width.value < 768 ? 14 : 18
    }
  },
  grid: {
    left: width.value < 768 ? '10%' : '3%',
    right: width.value < 768 ? '10%' : '4%',
    bottom: '3%',
    containLabel: true
  },
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    axisLabel: {
      fontSize: width.value < 768 ? 10 : 12
    }
  },
  yAxis: {
    type: 'value',
    axisLabel: {
      fontSize: width.value < 768 ? 10 : 12
    }
  },
  series: [
    {
      data: [150, 230, 224, 218, 135, 147, 260],
      type: 'line',
      smooth: true
    }
  ]
}))
</script>

<template>
  <VbenChart :option="option" height="400px" />
</template>

主题定制

vue
<script setup lang="ts">
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

// 自定义主题
const customTheme = {
  color: ['#1890ff', '#52c41a', '#faad14', '#f5222d', '#722ed1'],
  backgroundColor: '#fff',
  textStyle: {
    color: '#333'
  },
  title: {
    textStyle: {
      color: '#333'
    }
  },
  line: {
    itemStyle: {
      borderWidth: 2
    },
    lineStyle: {
      width: 2
    },
    symbolSize: 6,
    symbol: 'circle',
    smooth: true
  },
  bar: {
    itemStyle: {
      barBorderWidth: 0,
      barBorderColor: '#ccc'
    }
  }
}

const option: EChartsOption = {
  // 图表配置...
}
</script>

<template>
  <VbenChart 
    :option="option" 
    :theme="customTheme"
    height="400px" 
  />
</template>

数据更新

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenChart } from '@vben/common-ui'
import type { EChartsOption } from 'echarts'

const chartRef = ref()
const data = ref([150, 230, 224, 218, 135, 147, 260])

const option: EChartsOption = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: data.value,
      type: 'line'
    }
  ]
}

const updateData = () => {
  // 更新数据
  data.value = data.value.map(() => Math.floor(Math.random() * 300))
  
  // 更新图表
  chartRef.value?.setOption({
    series: [{ data: data.value }]
  })
}
</script>

<template>
  <div>
    <VbenChart ref="chartRef" :option="option" height="400px" />
    <a-button @click="updateData">更新数据</a-button>
  </div>
</template>

注意事项

注意

  1. 图表组件需要指定高度才能正常显示
  2. 大数据量时注意性能优化
  3. 响应式图表需要监听窗口大小变化
  4. 销毁组件时会自动销毁图表实例

提示

  • 使用 autoResize 属性自动调整图表大小
  • 可以通过 theme 属性自定义主题
  • 使用 loading 属性显示加载动画
  • 支持所有 ECharts 配置项和事件

相关链接

MIT License