前言:数据可视化的重要性
在当今数据驱动的时代,数据可视化已成为前端开发中不可或缺的一部分。无论是企业级应用还是个人项目,直观、美观的图表都能让数据"说话"。ECharts 作为百度开源的 JavaScript 可视化库,凭借其丰富的图表类型、灵活的配置选项和优秀的性能,成为了 Vue 项目中的首选图表解决方案。
TRAE IDE 提示:在 TRAE IDE 中,你可以通过智能代码补全功能快速生成 ECharts 的配置代码,大大提升开发效率。IDE 的实时预览功能还能让你即时看到图表效果,无需频繁切换浏览器窗口。
01|ECharts 集成:从零开始的项目搭建
1.1 安装依赖
在 Vue 项目中集成 ECharts 非常简单,首先我们需要安装必要的依赖包:
# 使用 npm 安装
npm install echarts vue-echarts
# 或者使用 yarn
yarn add echarts vue-echarts
# 使用 pnpm(推荐)
pnpm add echarts vue-echarts依赖说明:
echarts是核心图表库,vue-echarts是 Vue 的封装组件,提供了更友好的 Vue 使用体验。
1.2 全局注册组件
在 Vue 3 项目中,推荐在 main.js 或 main.ts 中进行全局注册:
import { createApp } from 'vue'
import App from './App.vue'
import ECharts from 'vue-echarts'
import { use } from 'echarts/core'
// 引入 ECharts 核心模块
import {
CanvasRenderer
} from 'echarts/renderers'
import {
BarChart,
LineChart,
PieChart,
ScatterChart
} from 'echarts/charts'
import {
GridComponent,
TooltipComponent,
TitleComponent,
LegendComponent,
ToolboxComponent
} from 'echarts/components'
// 注册必要的组件
use([
CanvasRenderer,
BarChart,
LineChart,
PieChart,
ScatterChart,
GridComponent,
TooltipComponent,
TitleComponent,
LegendComponent,
ToolboxComponent
])
const app = createApp(App)
// 全局注册 ECharts 组件
app.component('v-chart', ECharts)
app.mount('#app')1.3 TypeScript 支持
如果你的项目使用 TypeScript,需要在 shims-vue.d.ts 中添加类型声明:
declare module 'vue-echarts' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}02|基础图表创建:第一个 ECharts 组件
2.1 创建基础柱状图组件
让我们创建一个简单的柱状图组件来展示销售数据:
<template>
<div class="chart-container">
<v-chart :option="chartOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const chartOptions = ref({
title: {
text: '月度销售统计',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月']
},
yAxis: {
type: 'value',
name: '销售额(万元)'
},
series: [{
name: '销售额',
type: 'bar',
data: [120, 200, 150, 80, 70, 110],
itemStyle: {
color: '#5470c6'
},
emphasis: {
itemStyle: {
color: '#91cc75'
}
}
}]
})
</script>
<style scoped>
.chart-container {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>2.2 动态数据更新
实际项目中,图表数据通常来自 API 请求。让我们实现一个支持动态数据更新的图表:
<template>
<div class="dynamic-chart">
<div class="chart-header">
<h3>实时数据监控</h3>
<button @click="refreshData" :disabled="loading">
{{ loading ? '加载中...' : '刷新数据' }}
</button>
</div>
<v-chart :option="chartOptions" :style="{ height: '350px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
const loading = ref(false)
const chartOptions = reactive({
title: {
text: '用户访问趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
name: '访问量'
},
series: [{
name: '访问量',
type: 'line',
smooth: true,
areaStyle: {
opacity: 0.3
},
data: [],
itemStyle: {
color: '#5470c6'
}
}]
})
// 模拟 API 数据获取
const fetchData = async () => {
loading.value = true
try {
// 模拟 API 调用延迟
await new Promise(resolve => setTimeout(resolve, 1000))
const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`)
const visits = Array.from({ length: 24 }, () => Math.floor(Math.random() * 1000) + 200)
// 更新图表数据
chartOptions.xAxis.data = hours
chartOptions.series[0].data = visits
} catch (error) {
console.error('数据加载失败:', error)
} finally {
loading.value = false
}
}
const refreshData = () => {
fetchData()
}
onMounted(() => {
fetchData()
})
</script>
## 03|响应式图表:适配不同屏幕尺寸
### 3.1 自动响应式实现
ECharts 天生支持响应式设计,但在 Vue 中我们需要额外处理容器尺寸变化:
```vue
<template>
<div class="responsive-chart" ref="chartContainer">
<v-chart
:option="chartOptions"
:autoresize="true"
:style="{ height: containerHeight + 'px', width: '100%' }"
/>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
const chartContainer = ref(null)
const containerHeight = ref(400)
const chartOptions = reactive({
title: {
text: '响应式图表示例',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['销售额', '利润'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['Q1', 'Q2', 'Q3', 'Q4']
},
yAxis: {
type: 'value'
},
series: [
{
name: '销售额',
type: 'bar',
data: [320, 332, 301, 334],
itemStyle: { color: '#5470c6' }
},
{
name: '利润',
type: 'bar',
data: [120, 132, 101, 134],
itemStyle: { color: '#91cc75' }
}
]
})
// 响应式处理
const handleResize = () => {
if (chartContainer.value) {
const width = chartContainer.value.clientWidth
// 根据宽度调整图表高度
containerHeight.value = width < 768 ? 300 : 400
// 移动端优化
if (width < 768) {
chartOptions.legend.bottom = 5
chartOptions.grid.bottom = '20%'
} else {
chartOptions.legend.bottom = 10
chartOptions.grid.bottom = '15%'
}
}
}
let resizeObserver
onMounted(() => {
handleResize()
// 使用 ResizeObserver 监听容器尺寸变化
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(chartContainer.value)
} else {
// 降级方案:监听 window resize
window.addEventListener('resize', handleResize)
}
})
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect()
} else {
window.removeEventListener('resize', handleResize)
}
})
</script>
<style scoped>
.responsive-chart {
width: 100%;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>TRAE IDE 提示:TRAE IDE 的智能断点检测功能可以帮助你快速识别响应式设计中的断点问题,确保图表在各种设备上都能完美展示。
04|常用图表类型:丰富你的数据展示
4.1 饼图:占比分析利器
<template>
<div class="pie-chart-container">
<v-chart :option="pieOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const pieOptions = ref({
title: {
text: '产品销量占比',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '销量',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 1048, name: '电子产品' },
{ value: 735, name: '服装' },
{ value: 580, name: '食品' },
{ value: 484, name: '家居' },
{ value: 300, name: '其他' }
]
}
]
})
</script>4.2 散点图:相关性分析
<template>
<div class="scatter-chart-container">
<v-chart :option="scatterOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
// 生成模拟数据
const generateScatterData = () => {
const data = []
for (let i = 0; i < 100; i++) {
const x = Math.random() * 100
const y = x * 0.8 + Math.random() * 20 + 10
const size = Math.random() * 50 + 20
data.push([x, y, size])
}
return data
}
const scatterOptions = ref({
title: {
text: '用户活跃度与消费金额关系',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: function (params) {
return `活跃度: ${params.data[0].toFixed(2)}<br/>消费金额: ${params.data[1].toFixed(2)}<br/>用户等级: ${params.data[2].toFixed(0)}`
}
},
xAxis: {
name: '用户活跃度',
nameLocation: 'middle',
nameGap: 30
},
yAxis: {
name: '消费金额(元)',
nameLocation: 'middle',
nameGap: 30
},
series: [{
name: '用户数据',
type: 'scatter',
symbolSize: function (data) {
return data[2] / 2
},
data: generateScatterData(),
itemStyle: {
color: function(params) {
const value = params.data[2]
if (value > 60) return '#ee6666'
if (value > 40) return '#fac858'
return '#5470c6'
},
opacity: 0.8
}
}]
})
</script>4.3 组合图表:多维度数据展示
<template>
<div class="mixed-chart-container">
<v-chart :option="mixedOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const mixedOptions = ref({
title: {
text: '销售与利润分析',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['销售额', '利润', '利润率'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月']
},
yAxis: [
{
type: 'value',
name: '金额(万元)',
position: 'left'
},
{
type: 'value',
name: '利润率(%)',
position: 'right',
axisLabel: {
formatter: '{value}%'
}
}
],
series: [
{
name: '销售额',
type: 'bar',
data: [120, 200, 150, 80, 70, 110],
itemStyle: { color: '#5470c6' }
},
{
name: '利润',
type: 'bar',
data: [30, 50, 35, 20, 18, 28],
itemStyle: { color: '#91cc75' }
},
{
name: '利润率',
type: 'line',
yAxisIndex: 1,
data: [25, 25, 23, 25, 26, 25],
itemStyle: { color: '#fac858' },
lineStyle: {
width: 3
}
}
]
})
</script>05|性能优化:让图表飞起来
5.1 懒加载与按需加载
对于大型应用,我们应该按需加载 ECharts 模块,减少初始包体积:
// 创建 echarts.js 工具文件
import * as echarts from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { BarChart, LineChart, PieChart } from 'echarts/charts'
import {
GridComponent,
TooltipComponent,
TitleComponent,
LegendComponent
} from 'echarts/components'
// 只注册需要的组件
echarts.use([
CanvasRenderer,
BarChart,
LineChart,
PieChart,
GridComponent,
TooltipComponent,
TitleComponent,
LegendComponent
])
export default echarts5.2 大数据量优化
当数据量很大时,我们需要采用一些优化策略:
<template>
<div class="large-data-chart">
<v-chart :option="largeDataOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const largeDataOptions = ref({
title: {
text: '大数据量性能优化示例'
},
tooltip: {
trigger: 'axis',
// 大数据时禁用动画
transitionDuration: 0
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: [{
type: 'line',
data: [],
// 优化大数据渲染
large: true,
largeThreshold: 2000,
// 禁用动画
animation: false,
// 采样显示
sampling: 'average'
}]
})
// 生成大量数据
const generateLargeData = () => {
const categories = []
const data = []
for (let i = 0; i < 10000; i++) {
categories.push(`类别${i}`)
data.push(Math.random() * 1000)
}
return { categories, data }
}
onMounted(() => {
const { categories, data } = generateLargeData()
largeDataOptions.value.xAxis.data = categories
largeDataOptions.value.series[0].data = data
})
</script>5.3 内存优化
及时销毁不需要的图表实例,避免内存泄漏:
<template>
<div class="memory-optimized-chart">
<v-chart
ref="chartRef"
:option="chartOptions"
:style="{ height: '400px', width: '100%' }"
/>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const chartRef = ref(null)
const chartOptions = ref({
// 图表配置...
})
let updateInterval
onMounted(() => {
// 定期更新数据
updateInterval = setInterval(() => {
// 更新逻辑
}, 5000)
})
onUnmounted(() => {
// 清理定时器
if (updateInterval) {
clearInterval(updateInterval)
}
// 销毁图表实例
if (chartRef.value) {
chartRef.value.dispose()
}
})
</script>5.4 渲染优化技巧
// 批量更新数据
const batchUpdateChart = (chart, newData) => {
// 使用 setOption 的 notMerge 参数
chart.setOption({
series: [{
data: newData
}]
}, {
notMerge: false, // 不合并配置
lazyUpdate: true // 延迟更新
})
}
// 防抖处理频繁更新
const debounceUpdate = (func, wait) => {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
// 使用防抖
const updateChart = debounceUpdate((newData) => {
chart.setOption({
series: [{ data: newData }]
})
}, 300)06|TRAE IDE 集成:提升开发体验
6.1 智能代码补全
TRAE IDE 提供了强大的智能代码补全功能,在编写 ECharts 配置时特别有用:
// 输入 "title" 后,IDE 会自动提示完整的 title 配置结构
const chartOptions = {
title: {
text: '主标题',
subtext: '副标题', // IDE 自动提示
left: 'center', // IDE 提供枚举值提示
textStyle: { // IDE 自动补全嵌套结构
fontSize: 18,
fontWeight: 'bold'
}
}
}6.2 实时预览与调试
TRAE IDE 的实时预览功能让你无需刷新浏览器就能看到图表变化:
开发建议:
- 使用 TRAE IDE 的分屏功能,一边编写代码,一边查看图表效果
- 利用 IDE 的调试工具,可以实时查看 chartOptions 的数据变化
- 通过 IDE 的性能分析工具,监控图表渲染性能
6.3 代码片段与模板
在 TRAE IDE 中创建常用的 ECharts 代码片段:
{
"ECharts Base Config": {
"prefix": "echarts-base",
"body": [
"const chartOptions = {",
" title: {",
" text: '${1:图表标题}',",
" left: 'center'",
" },",
" tooltip: {",
" trigger: '${2:axis}'",
" },",
" xAxis: {",
" type: '${3:category}',",
" data: ${4:[]}",
" },",
" yAxis: {",
" type: '${5:value}'",
" },",
" series: [{",
" name: '${6:系列名称}',",
" type: '${7:bar}',",
" data: ${8:[]}",
" }]",
"}"
]
}
}6.4 项目管理与协作
TRAE IDE 提供了优秀的项目管理功能:
# 使用 TRAE IDE 的终端快速创建组件
touch src/components/Charts/SalesChart.vue
# IDE 会自动识别 Vue 文件并提供相应的语法高亮
# 使用 Git 集成进行版本控制
git add .
git commit -m "feat: 添加销售数据图表组件"TRAE IDE 亮点总结:
- 智能感知:自动识别 ECharts 配置项,提供精准的代码补全
- 实时协作:多人同时编辑图表组件,实时同步变更
- 性能监控:内置性能分析工具,帮助优化图表渲染
- 一键部署:集成 CI/CD 流程,图表更新自动部署
07|最佳实践与常见问题
7.1 组件化最佳实践
<!-- ChartWrapper.vue - 通用图表包装组件 -->
<template>
<div class="chart-wrapper" :class="{ 'is-loading': loading }">
<div v-if="loading" class="chart-loading">
<span>数据加载中...</span>
</div>
<div v-else-if="error" class="chart-error">
<span>{{ error }}</span>
<button @click="$emit('retry')">重试</button>
</div>
<v-chart
v-else
:option="options"
:autoresize="autoresize"
:style="{ height: height + 'px', width: '100%' }"
@finished="handleChartFinished"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
defineProps({
options: {
type: Object,
required: true
},
loading: {
type: Boolean,
default: false
},
error: {
type: String,
default: ''
},
height: {
type: Number,
default: 400
},
autoresize: {
type: Boolean,
default: true
}
})
defineEmits(['retry'])
const handleChartFinished = () => {
console.log('图表渲染完成')
}
</script>7.2 常见问题解决方案
问题 1:图表不显示
// 确保容器有明确的尺寸
const checkContainerSize = () => {
const container = document.querySelector('.chart-container')
if (container && container.clientWidth === 0) {
console.warn('图表容器宽度为0,请检查CSS样式')
}
}问题 2:内存泄漏
// 正确的销毁方式
const disposeChart = (chart) => {
if (chart && !chart.isDisposed()) {
chart.dispose()
}
}问题 3:性能问题
// 使用防抖和节流
import { debounce, throttle } from 'lodash-es'
const optimizedResize = debounce(() => {
chart.resize()
}, 300)
const optimizedUpdate = throttle((data) => {
chart.setOption({ series: [{ data }] })
}, 100)总结与展望
通过本文的学习,我们全面掌握了在 Vue 项目中集成和使用 ECharts 的方法:
- 基础集成:从安装依赖到全局注册,为项目添加图表能力
- 图表创建:学会创建各种类型的图表,满足不同数据展示需求
- 响应式设计:让图表在各种设备上都能完美展示
- 性能优化:掌握大数据量和复杂场景下的优化技巧
- 开发工具:利用 TRAE IDE 提升开发效率和代码质量
思考题:
- 在你的项目中,哪种图表类型最适合展示你的业务数据?
- 如何结合 TRAE IDE 的智能提示功能,快速构建复杂的图表配置?
- 面对海量数据,你会采用哪些性能优化策略?
ECharts 的强大功能远不止于此,结合 Vue 的响应式特性和 TRAE IDE 的智能开发体验,我们可以构建出更加专业、高效的数据可视化应用。希望本文能帮助你在数据可视化的道路上走得更远!
03|响应式图表:适配不同屏幕尺寸
3.1 自动响应式实现
ECharts 天生支持响应式设计,但在 Vue 中我们需要额外处理容器尺寸变化:
<template>
<div class="responsive-chart" ref="chartContainer">
<v-chart
:option="chartOptions"
:autoresize="true"
:style="{ height: containerHeight + 'px', width: '100%' }"
/>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
const chartContainer = ref(null)
const containerHeight = ref(400)
const chartOptions = reactive({
title: {
text: '响应式图表示例',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['销售额', '利润'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['Q1', 'Q2', 'Q3', 'Q4']
},
yAxis: {
type: 'value'
},
series: [
{
name: '销售额',
type: 'bar',
data: [320, 332, 301, 334],
itemStyle: { color: '#5470c6' }
},
{
name: '利润',
type: 'bar',
data: [120, 132, 101, 134],
itemStyle: { color: '#91cc75' }
}
]
})
// 响应式处理
const handleResize = () => {
if (chartContainer.value) {
const width = chartContainer.value.clientWidth
// 根据宽度调整图表高度
containerHeight.value = width < 768 ? 300 : 400
// 移动端优化
if (width < 768) {
chartOptions.legend.bottom = 5
chartOptions.grid.bottom = '20%'
} else {
chartOptions.legend.bottom = 10
chartOptions.grid.bottom = '15%'
}
}
}
let resizeObserver
onMounted(() => {
handleResize()
// 使用 ResizeObserver 监听容器尺寸变化
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(chartContainer.value)
} else {
// 降级方案:监听 window resize
window.addEventListener('resize', handleResize)
}
})
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect()
} else {
window.removeEventListener('resize', handleResize)
}
})
</script>
<style scoped>
.responsive-chart {
width: 100%;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>TRAE IDE 提示:TRAE IDE 的智能断点检测功能可以帮助你快速识别响应式设计中的断点问题,确保图表在各种设备上都能完美展示。
04|常用图表类型:丰富你的数据展示
4.1 饼图:占比分析利器
<template>
<div class="pie-chart-container">
<v-chart :option="pieOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const pieOptions = ref({
title: {
text: '产品销量占比',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '销量',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 1048, name: '电子产品' },
{ value: 735, name: '服装' },
{ value: 580, name: '食品' },
{ value: 484, name: '家居' },
{ value: 300, name: '其他' }
]
}
]
})
</script>4.2 散点图:相关性分析
<template>
<div class="scatter-chart-container">
<v-chart :option="scatterOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
// 生成模拟数据
const generateScatterData = () => {
const data = []
for (let i = 0; i < 100; i++) {
const x = Math.random() * 100
const y = x * 0.8 + Math.random() * 20 + 10
const size = Math.random() * 50 + 20
data.push([x, y, size])
}
return data
}
const scatterOptions = ref({
title: {
text: '用户活跃度与消费金额关系',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: function (params) {
return `活跃度: ${params.data[0].toFixed(2)}<br/>消费金额: ${params.data[1].toFixed(2)}<br/>用户等级: ${params.data[2].toFixed(0)}`
}
},
xAxis: {
name: '用户活跃度',
nameLocation: 'middle',
nameGap: 30
},
yAxis: {
name: '消费金额(元)',
nameLocation: 'middle',
nameGap: 30
},
series: [{
name: '用户数据',
type: 'scatter',
symbolSize: function (data) {
return data[2] / 2
},
data: generateScatterData(),
itemStyle: {
color: function(params) {
const value = params.data[2]
if (value > 60) return '#ee6666'
if (value > 40) return '#fac858'
return '#5470c6'
},
opacity: 0.8
}
}]
})
</script>4.3 组合图表:多维度数据展示
<template>
<div class="mixed-chart-container">
<v-chart :option="mixedOptions" :style="{ height: '400px', width: '100%' }" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const mixedOptions = ref({
title: {
text: '销售与利润分析',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['销售额', '利润', '利润率'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月']
},
yAxis: [
{
type: 'value',
name: '金额(万元)',
position: 'left'
},
{
type: 'value',
name: '利润率(%)',
position: 'right',
axisLabel: {
formatter: '{value}%'
}
}
],
series: [
{
name: '销售额',
type: 'bar',
data: [120, 200, 150, 80, 70, 110],
itemStyle: { color: '#5470c6' }
},
{
name: '利润',
type: 'bar',
data: [30, 50, 35, 20, 18, 28],
itemStyle: { color: '#91cc75' }
},
{
name: '利润率',
type: 'line',
yAxisIndex: 1,
data: [25, 25, 23, 25, 26, 25],
itemStyle: { color: '#fac858' },
lineStyle: {
width: 3
}
}
]
})
</script>(此内容由 AI 辅助生成,仅供参考)