Skip to content

图表组件

图表组件基于 ECharts 封装,提供了简单易用的图表渲染功能,支持主题适配和响应式布局。

导入

typescript
import { DuxChart } from '@duxweb/dvha-pro'

组件总览

DVHA Pro 提供以下图表组件:

  • DuxChart - 通用图表组件

DuxChart 通用图表组件

基于 ECharts 的通用图表组件,支持多种图表类型,包括柱状图、折线图、饼图、雷达图等。

属性

属性名类型默认值说明
heightstring'50px'图表高度
minbooleanfalse是否为最小化模式
typestring'bar'图表类型
optionobject{}图表配置选项
classstring''自定义样式类名

图表类型

类型说明
bar柱状图
line折线图
pie饼图
ring环形图
rose玫瑰图
radar雷达图
map地图
funnel漏斗图
treemap矩形树图

基础用法

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

// 柱状图数据
const barData = ref([
  { name: '周一', value: 120 },
  { name: '周二', value: 200 },
  { name: '周三', value: 150 },
  { name: '周四', value: 80 },
  { name: '周五', value: 70 }
])
</script>

<template>
  <!-- 基础柱状图 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{ data: barData }"
  />
</template>

图表类型详解

柱状图 (bar)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

// 单系列数据
const singleData = ref([
  { name: '产品A', value: 320 },
  { name: '产品B', value: 240 },
  { name: '产品C', value: 149 },
  { name: '产品D', value: 100 }
])

// 多系列数据
const multiData = ref([
  { name: '销售额', data: [320, 240, 149, 100, 80] },
  { name: '利润', data: [120, 80, 70, 40, 30] }
])

const labels = ref(['1月', '2月', '3月', '4月', '5月'])
</script>

<template>
  <!-- 基础柱状图 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{ data: singleData }"
  />

  <!-- 多系列柱状图 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{
      data: multiData,
      labels,
    }"
  />

  <!-- 水平柱状图 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{
      data: singleData,
      horizontal: true,
    }"
  />

  <!-- 堆叠柱状图 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{
      data: multiData,
      labels,
      stack: 'total',
    }"
  />
</template>

折线图 (line)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const lineData = ref([
  { name: '访问量', data: [120, 132, 101, 134, 90, 230, 210] },
  { name: '下载量', data: [220, 182, 191, 234, 290, 330, 310] }
])

const labels = ref(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
</script>

<template>
  <!-- 基础折线图 -->
  <DuxChart
    type="line"
    height="300px"
    :option="{
      data: lineData,
      labels,
    }"
  />

  <!-- 平滑折线图 -->
  <DuxChart
    type="line"
    height="300px"
    :option="{
      data: lineData,
      labels,
      smooth: true,
    }"
  />

  <!-- 面积图 -->
  <DuxChart
    type="line"
    height="300px"
    :option="{
      data: lineData,
      labels,
      stack: 'total',
    }"
  />
</template>

饼图 (pie)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const pieData = ref([
  { name: 'Chrome', value: 335 },
  { name: 'Safari', value: 310 },
  { name: 'Firefox', value: 234 },
  { name: 'Edge', value: 135 },
  { name: 'Others', value: 48 }
])
</script>

<template>
  <!-- 基础饼图 -->
  <DuxChart
    type="pie"
    height="300px"
    :option="{ data: pieData }"
  />

  <!-- 最小化饼图 -->
  <DuxChart
    type="pie"
    height="150px"
    min
    :option="{ data: pieData }"
  />
</template>

环形图 (ring)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const ringData = ref([
  { name: '完成', value: 75 },
  { name: '进行中', value: 15 },
  { name: '待开始', value: 10 }
])
</script>

<template>
  <!-- 环形图 -->
  <DuxChart
    type="ring"
    height="300px"
    :option="{
      data: ringData,
      ringLabel: '项目进度',
    }"
  />
</template>

雷达图 (radar)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const radarData = ref([
  { name: '产品A', value: [4300, 10000, 28000, 35000, 50000, 19000] },
  { name: '产品B', value: [5000, 14000, 28000, 31000, 42000, 21000] }
])

const indicator = ref([
  { name: '销售', max: 6500 },
  { name: '管理', max: 16000 },
  { name: '信息技术', max: 30000 },
  { name: '客服', max: 38000 },
  { name: '研发', max: 52000 },
  { name: '市场', max: 25000 }
])
</script>

<template>
  <!-- 雷达图 -->
  <DuxChart
    type="radar"
    height="300px"
    :option="{
      data: radarData,
      indicator,
    }"
  />
</template>

漏斗图 (funnel)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const funnelData = ref([
  { name: '访问', value: 60 },
  { name: '咨询', value: 40 },
  { name: '订单', value: 20 },
  { name: '点击', value: 80 },
  { name: '展现', value: 100 }
])
</script>

<template>
  <!-- 漏斗图 -->
  <DuxChart
    type="funnel"
    height="300px"
    :option="{ data: funnelData }"
  />
</template>

矩形树图 (treemap)

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const treeMapData = ref([
  {
    name: '技术部',
    value: 100,
    children: [
      { name: '前端', value: 40 },
      { name: '后端', value: 35 },
      { name: '测试', value: 25 }
    ]
  },
  {
    name: '产品部',
    value: 80,
    children: [
      { name: '产品经理', value: 50 },
      { name: '设计师', value: 30 }
    ]
  }
])
</script>

<template>
  <!-- 矩形树图 -->
  <DuxChart
    type="treemap"
    height="300px"
    :option="{ data: treeMapData }"
  />
</template>

配置选项

通用配置

vue
<script setup lang="ts">
const commonOptions = ref({
  // 是否显示图例
  showLegend: true,
  // 是否显示网格线
  showGridLine: true,
  // 是否显示 X 轴标签
  showXAxisLabel: true,
  // 是否显示 Y 轴标签
  showYAxisLabel: true,
  // 是否显示 X 轴线
  showXAxisLine: true,
  // 是否显示 Y 轴线
  showYAxisLine: true,
  // 是否显示分割区域
  showSplitArea: false
})
</script>

<template>
  <DuxChart
    type="bar"
    height="300px"
    :option="{
      data: chartData,
      ...commonOptions,
    }"
  />
</template>

最小化模式

vue
<template>
  <!-- 最小化模式,适用于仪表盘等场景 -->
  <DuxChart
    type="line"
    height="80px"
    min
    :option="{ data: miniData }"
  />
</template>

自定义 ECharts 配置

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const customOption = ref({
  data: chartData,
  // 自定义颜色
  color: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'],
  // 自定义标题
  title: {
    text: '自定义图表',
    left: 'center'
  },
  // 自定义工具箱
  toolbox: {
    show: true,
    feature: {
      saveAsImage: {}
    }
  }
})
</script>

<template>
  <DuxChart
    type="bar"
    height="400px"
    :option="customOption"
  />
</template>

响应式设计

自适应容器

vue
<template>
  <div class="chart-container">
    <!-- 图表会自动适应容器大小 -->
    <DuxChart
      type="line"
      height="100%"
      :option="{ data: chartData }"
    />
  </div>
</template>

<style scoped>
.chart-container {
  width: 100%;
  height: 400px;
  min-height: 200px;
}

@media (max-width: 768px) {
  .chart-container {
    height: 300px;
  }
}
</style>

响应式配置

vue
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
import { computed } from 'vue'

const { width } = useWindowSize()

const responsiveOption = computed(() => ({
  data: chartData.value,
  showLegend: width.value > 768,
  showXAxisLabel: width.value > 480,
  showYAxisLabel: width.value > 480
}))
</script>

<template>
  <DuxChart
    type="bar"
    :height="width > 768 ? '400px' : '300px'"
    :option="responsiveOption"
  />
</template>

实时数据

动态更新

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { onMounted, onUnmounted, ref } from 'vue'

const realtimeData = ref([
  { name: '实时数据', data: [] }
])

let timer: NodeJS.Timeout

function updateData() {
  const newValue = Math.floor(Math.random() * 100)
  const currentData = realtimeData.value[0].data

  currentData.push(newValue)

  // 保持最多 20 个数据点
  if (currentData.length > 20) {
    currentData.shift()
  }
}

onMounted(() => {
  timer = setInterval(updateData, 1000)
})

onUnmounted(() => {
  if (timer) {
    clearInterval(timer)
  }
})
</script>

<template>
  <DuxChart
    type="line"
    height="300px"
    :option="{
      data: realtimeData,
      labels: Array.from({ length: 20 }, (_, i) => i + 1),
    }"
  />
</template>

事件处理

图表交互

vue
<script setup lang="ts">
import { DuxChart } from '@duxweb/dvha-pro'
import { ref } from 'vue'

const chartRef = ref()

function handleChartClick(params: any) {
  console.log('图表点击事件:', params)
}

function handleLegendClick(params: any) {
  console.log('图例点击事件:', params)
}

onMounted(() => {
  if (chartRef.value) {
    // 绑定事件
    chartRef.value.on('click', handleChartClick)
    chartRef.value.on('legendselectchanged', handleLegendClick)
  }
})
</script>

<template>
  <DuxChart
    ref="chartRef"
    type="pie"
    height="300px"
    :option="{ data: pieData }"
  />
</template>

主题定制

内置主题

vue
<template>
  <!-- 图表会自动适配系统主题 -->
  <DuxChart
    type="bar"
    height="300px"
    :option="{ data: chartData }"
  />
</template>

自定义主题

vue
<script setup lang="ts">
// 自定义主题色彩
const customTheme = {
  color: [
    '#FF6B6B',
    '#4ECDC4',
    '#45B7D1',
    '#96CEB4',
    '#FECA57',
    '#FF9FF3',
    '#54A0FF',
    '#5F27CD'
  ],
  backgroundColor: 'transparent',
  textStyle: {
    color: '#333'
  }
}
</script>

<template>
  <DuxChart
    type="pie"
    height="300px"
    :option="{
      data: chartData,
      ...customTheme,
    }"
  />
</template>

最佳实践

1. 合理选择图表类型

vue
<template>
  <!-- 数值比较 - 使用柱状图 -->
  <DuxChart type="bar" :option="{ data: comparisonData }" />

  <!-- 趋势变化 - 使用折线图 -->
  <DuxChart type="line" :option="{ data: trendData }" />

  <!-- 比例关系 - 使用饼图 -->
  <DuxChart type="pie" :option="{ data: proportionData }" />

  <!-- 多维度分析 - 使用雷达图 -->
  <DuxChart type="radar" :option="{ data: radarData }" />
</template>

2. 优化性能

vue
<script setup lang="ts">
import { computed, shallowRef } from 'vue'

// 使用 shallowRef 避免深度响应式
const chartData = shallowRef([])

// 使用 computed 缓存配置
const chartOption = computed(() => ({
  data: chartData.value,
  showLegend: true
}))

// 大数据集分页显示
const pagedData = computed(() => {
  const pageSize = 100
  return chartData.value.slice(0, pageSize)
})
</script>

3. 无障碍访问

vue
<template>
  <div role="img" aria-label="销售数据图表">
    <DuxChart
      type="bar"
      height="300px"
      :option="{
        data: salesData,
        aria: {
          enabled: true,
          decal: {
            show: true,
          },
        },
      }"
    />
  </div>
</template>

4. 错误处理

vue
<script setup lang="ts">
import { computed } from 'vue'

const safeChartData = computed(() => {
  try {
    return processChartData(rawData.value)
  }
  catch (error) {
    console.error('图表数据处理错误:', error)
    return []
  }
})

const hasData = computed(() => {
  return safeChartData.value && safeChartData.value.length > 0
})
</script>

<template>
  <div v-if="hasData">
    <DuxChart
      type="bar"
      height="300px"
      :option="{ data: safeChartData }"
    />
  </div>
  <div v-else class="text-center text-gray-500 py-8">
    暂无数据
  </div>
</template>