10.数据可视化

1. 为什么需要数据可视化?

1.1 数据的价值

数据 = 信息,但原始数据很难理解

生活类比

  • 原始数据:1000个数字(看不懂)
  • 可视化:把这些数字变成图表(一眼看懂)

实际例子

1
2
3
4
5
6
7
8
原始数据:
2024年1月:1000
2024年2月:1500
2024年3月:1200
...

可视化后:
📊 折线图:一眼看出趋势(2月最高,3月下降)

1.2 数据可视化的作用

作用 说明 实际案例
直观展示 用图表代替数字 销售趋势图、用户增长图
发现规律 一眼看出数据规律 哪个时间段访问量最高
辅助决策 帮助做出更好的决策 根据销售数据调整策略
提升体验 让数据更有趣、更易理解 数据看板、统计报表

💡 记忆技巧
数据可视化 = 把"数字"变成"图画",让人一眼就能看懂!


1.3 常见的数据可视化场景

场景 图表类型 示例
趋势分析 折线图 销售额随时间的变化
对比分析 柱状图 不同产品的销量对比
占比分析 饼图 不同类别的占比
分布分析 散点图 用户年龄分布
综合展示 数据看板 多个图表组合展示

2. G2Plot图表库:快速上手

2.1 什么是G2Plot?

G2Plot = 基于G2(AntV)的图表库,专为业务场景设计

G2 = 更底层的图形语法库(更灵活,但更复杂)

G2Plot = 基于G2封装的高级图表库(简单易用,适合业务场景)

关系

1
G2(底层) → G2Plot(高级封装) → Ant Design Vue(UI组件库集成)

优势

优势 说明
简单易用 API简洁,上手快
图表丰富 支持20+种图表类型
自动适配 自动响应式,适配不同屏幕
与Ant Design Vue集成 完美配合,风格统一

2.2 安装G2Plot

安装命令

1
npm install @antv/g2plot

在Vue中使用

1
import { Line, Column, Pie } from '@antv/g2plot';


2.3 基础图表:折线图

2.3.1 创建折线图

作用:展示数据随时间的变化趋势

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<div id="lineChart" style="width: 600px; height: 300px;"></div>
</template>

<script>
import { Line } from '@antv/g2plot';
import { onMounted } from 'vue';

export default {
setup() {
onMounted(() => {
// 数据
const data = [
{ month: '1月', value: 1000 },
{ month: '2月', value: 1500 },
{ month: '3月', value: 1200 },
{ month: '4月', value: 1800 },
{ month: '5月', value: 2000 }
];

// 创建折线图
const line = new Line('lineChart', {
data,
xField: 'month', // X轴字段
yField: 'value', // Y轴字段
point: {
size: 5,
shape: 'circle'
}
});

// 渲染图表
line.render();
});
}
};
</script>

效果:显示一条折线,展示数据的变化趋势


2.3.2 多条折线图

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const data = [
{ month: '1月', 销售额: 1000, 利润: 200 },
{ month: '2月', 销售额: 1500, 利润: 300 },
{ month: '3月', 销售额: 1200, 利润: 250 }
];

const line = new Line('lineChart', {
data,
xField: 'month',
yField: ['销售额', '利润'], // 多个Y轴字段
legend: {
position: 'top'
}
});

line.render();


2.4 柱状图

2.4.1 基础柱状图

作用:对比不同类别的数据

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Column } from '@antv/g2plot';

const data = [
{ 产品: '产品A', 销量: 100 },
{ 产品: '产品B', 销量: 150 },
{ 产品: '产品C', 销量: 120 }
];

const column = new Column('columnChart', {
data,
xField: '产品',
yField: '销量',
color: '#3498db'
});

column.render();


2.4.2 分组柱状图

作用:对比多个维度的数据

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const data = [
{ 月份: '1月', 产品A: 100, 产品B: 80 },
{ 月份: '2月', 产品A: 150, 产品B: 120 },
{ 月份: '3月', 产品A: 120, 产品B: 100 }
];

const column = new Column('columnChart', {
data,
xField: '月份',
yField: ['产品A', '产品B'],
isGroup: true // 分组显示
});

column.render();


2.5 饼图

2.5.1 基础饼图

作用:展示数据的占比关系

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Pie } from '@antv/g2plot';

const data = [
{ type: '类型A', value: 30 },
{ type: '类型B', value: 25 },
{ type: '类型C', value: 20 },
{ type: '类型D', value: 25 }
];

const pie = new Pie('pieChart', {
data,
angleField: 'value', // 角度字段
colorField: 'type', // 颜色字段
label: {
type: 'outer',
content: '{name} {percentage}'
}
});

pie.render();


2.6 图表配置选项

常用配置

配置项 说明 示例
title 图表标题 title: { text: '销售趋势' }
legend 图例 legend: { position: 'top' }
color 颜色 color: '#3498db'
label 标签 label: { position: 'top' }
tooltip 提示框 tooltip: { showMarkers: true }

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const line = new Line('lineChart', {
data,
xField: 'month',
yField: 'value',
title: {
text: '销售趋势图',
style: {
fontSize: 16
}
},
legend: {
position: 'top-right'
},
color: '#3498db',
tooltip: {
showMarkers: true
}
});


3. 数据看板开发

3.1 什么是数据看板?

数据看板 = 多个图表组合在一起,全面展示数据

生活类比

  • 单个图表 = 一张照片(只能看一个角度)
  • 数据看板 = 多张照片拼成的全景图(能看到全貌)

实际例子

1
2
3
4
数据看板包含:
- 顶部:关键指标卡片(总销售额、总用户数等)
- 中间:多个图表(销售趋势、产品分布等)
- 底部:数据表格(详细数据)

3.2 看板布局设计

3.2.1 使用Ant Design Vue布局

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<a-layout>
<a-layout-header>
<h1>数据看板</h1>
</a-layout-header>
<a-layout-content style="padding: 24px;">
<!-- 关键指标 -->
<a-row :gutter="16" style="margin-bottom: 24px;">
<a-col :span="6">
<a-card>
<h3>总销售额</h3>
<p style="font-size: 24px;">¥100,000</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card>
<h3>总用户数</h3>
<p style="font-size: 24px;">1,234</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card>
<h3>订单数</h3>
<p style="font-size: 24px;">567</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card>
<h3>增长率</h3>
<p style="font-size: 24px;">+12.5%</p>
</a-card>
</a-col>
</a-row>

<!-- 图表区域 -->
<a-row :gutter="16">
<a-col :span="12">
<a-card title="销售趋势">
<div id="lineChart"></div>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="产品分布">
<div id="pieChart"></div>
</a-card>
</a-col>
</a-row>
</a-layout-content>
</a-layout>
</template>


3.3 完整看板示例

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<template>
<div class="dashboard">
<h1>数据看板</h1>

<!-- 关键指标卡片 -->
<a-row :gutter="16" class="stats-row">
<a-col :span="6" v-for="stat in stats" :key="stat.title">
<a-card>
<h3>{{ stat.title }}</h3>
<p class="stat-value">{{ stat.value }}</p>
<span :class="stat.trend > 0 ? 'trend-up' : 'trend-down'">
{{ stat.trend > 0 ? '↑' : '↓' }} {{ Math.abs(stat.trend) }}%
</span>
</a-card>
</a-col>
</a-row>

<!-- 图表区域 -->
<a-row :gutter="16" class="charts-row">
<a-col :span="12">
<a-card title="销售趋势">
<div id="salesChart" style="height: 300px;"></div>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="产品销量">
<div id="productChart" style="height: 300px;"></div>
</a-card>
</a-col>
</a-row>
</div>
</template>

<script>
import { Line, Column } from '@antv/g2plot';
import { onMounted, ref } from 'vue';

export default {
setup() {
const stats = ref([
{ title: '总销售额', value: '¥100,000', trend: 12.5 },
{ title: '总用户数', value: '1,234', trend: 8.3 },
{ title: '订单数', value: '567', trend: -2.1 },
{ title: '增长率', value: '+12.5%', trend: 5.2 }
]);

onMounted(() => {
// 销售趋势图
const salesData = [
{ month: '1月', value: 1000 },
{ month: '2月', value: 1500 },
{ month: '3月', value: 1200 }
];
const salesChart = new Line('salesChart', {
data: salesData,
xField: 'month',
yField: 'value'
});
salesChart.render();

// 产品销量图
const productData = [
{ 产品: '产品A', 销量: 100 },
{ 产品: '产品B', 销量: 150 },
{ 产品: '产品C', 销量: 120 }
];
const productChart = new Column('productChart', {
data: productData,
xField: '产品',
yField: '销量'
});
productChart.render();
});

return {
stats
};
}
};
</script>

<style scoped>
.stat-value {
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.trend-up {
color: #52c41a;
}
.trend-down {
color: #ff4d4f;
}
</style>


4. 动态数据更新

4.1 实时更新图表数据

场景:数据从API获取,需要实时更新图表

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { Line } from '@antv/g2plot';
import { onMounted, ref } from 'vue';
import { getSalesData } from '@/api/sales';

export default {
setup() {
const chartRef = ref(null);

onMounted(async () => {
// 初始化图表
const chart = new Line('chart', {
data: [],
xField: 'month',
yField: 'value'
});
chart.render();
chartRef.value = chart;

// 加载数据
await loadData();
});

// 加载数据
const loadData = async () => {
try {
const data = await getSalesData();
// 更新图表数据
chartRef.value.update({
data: data
});
} catch (error) {
console.error('加载数据失败', error);
}
};

return {
loadData
};
}
};


4.2 定时刷新数据

作用:每隔一段时间自动更新数据

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { onMounted, onUnmounted } from 'vue';

export default {
setup() {
let timer = null;

onMounted(() => {
// 立即加载一次
loadData();

// 每30秒刷新一次
timer = setInterval(() => {
loadData();
}, 30000);
});

onUnmounted(() => {
// 组件销毁时清除定时器
if (timer) {
clearInterval(timer);
}
});
}
};


4.3 响应式更新

作用:窗口大小改变时,图表自动调整

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { onMounted, onUnmounted } from 'vue';

export default {
setup() {
const chartRef = ref(null);

const handleResize = () => {
if (chartRef.value) {
chartRef.value.changeSize(
document.getElementById('chart').offsetWidth,
300
);
}
};

onMounted(() => {
window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
}
};


5. AI实战:使用AI生成数据看板

5.1 任务描述

目标:使用AI工具生成一个完整的数据看板

功能要求

  • 使用Ant Design Vue布局
  • 显示关键指标卡片(4个)
  • 包含折线图(销售趋势)
  • 包含柱状图(产品销量)
  • 包含饼图(类别分布)
  • 数据从API获取(模拟数据也可以)
  • 样式美观,响应式布局

5.2 第一步:设计Prompt

Prompt示例

1
2
3
4
5
6
7
8
9
10
用Vue 3和Ant Design Vue创建一个数据看板,要求:
1. 使用Ant Design Vue的Layout和Card组件
2. 顶部显示4个关键指标卡片(总销售额、总用户数、订单数、增长率)
3. 中间区域包含3个图表:
- 折线图:销售趋势(使用G2Plot)
- 柱状图:产品销量(使用G2Plot)
- 饼图:类别分布(使用G2Plot)
4. 使用响应式布局,适配不同屏幕
5. 图表数据使用模拟数据
6. 代码注释清晰,样式美观


5.3 AI生成的代码(示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<template>
<a-layout class="dashboard">
<a-layout-header class="header">
<h1>数据看板</h1>
</a-layout-header>
<a-layout-content class="content">
<!-- 关键指标 -->
<a-row :gutter="16" class="stats-row">
<a-col :xs="24" :sm="12" :md="6" v-for="stat in stats" :key="stat.title">
<a-card class="stat-card">
<h3>{{ stat.title }}</h3>
<p class="stat-value">{{ stat.value }}</p>
<span :class="stat.trend > 0 ? 'trend-up' : 'trend-down'">
{{ stat.trend > 0 ? '↑' : '↓' }} {{ Math.abs(stat.trend) }}%
</span>
</a-card>
</a-col>
</a-row>

<!-- 图表区域 -->
<a-row :gutter="16" class="charts-row">
<a-col :xs="24" :sm="24" :md="12">
<a-card title="销售趋势">
<div id="salesChart" style="height: 300px;"></div>
</a-card>
</a-col>
<a-col :xs="24" :sm="24" :md="12">
<a-card title="产品销量">
<div id="productChart" style="height: 300px;"></div>
</a-card>
</a-col>
</a-row>

<a-row :gutter="16" style="margin-top: 16px;">
<a-col :xs="24" :sm="24" :md="12">
<a-card title="类别分布">
<div id="categoryChart" style="height: 300px;"></div>
</a-card>
</a-col>
</a-row>
</a-layout-content>
</a-layout>
</template>

<script>
import { Line, Column, Pie } from '@antv/g2plot';
import { onMounted, ref } from 'vue';

export default {
setup() {
const stats = ref([
{ title: '总销售额', value: '¥100,000', trend: 12.5 },
{ title: '总用户数', value: '1,234', trend: 8.3 },
{ title: '订单数', value: '567', trend: -2.1 },
{ title: '增长率', value: '+12.5%', trend: 5.2 }
]);

onMounted(() => {
// 销售趋势图
const salesData = [
{ month: '1月', value: 1000 },
{ month: '2月', value: 1500 },
{ month: '3月', value: 1200 },
{ month: '4月', value: 1800 },
{ month: '5月', value: 2000 }
];
const salesChart = new Line('salesChart', {
data: salesData,
xField: 'month',
yField: 'value',
smooth: true,
color: '#3498db'
});
salesChart.render();

// 产品销量图
const productData = [
{ 产品: '产品A', 销量: 100 },
{ 产品: '产品B', 销量: 150 },
{ 产品: '产品C', 销量: 120 },
{ 产品: '产品D', 销量: 80 }
];
const productChart = new Column('productChart', {
data: productData,
xField: '产品',
yField: '销量',
color: '#52c41a'
});
productChart.render();

// 类别分布图
const categoryData = [
{ type: '类别A', value: 30 },
{ type: '类别B', value: 25 },
{ type: '类别C', value: 20 },
{ type: '类别D', value: 25 }
];
const categoryChart = new Pie('categoryChart', {
data: categoryData,
angleField: 'value',
colorField: 'type',
label: {
type: 'outer',
content: '{name} {percentage}'
}
});
categoryChart.render();
});

return {
stats
};
}
};
</script>

<style scoped>
.dashboard {
min-height: 100vh;
}
.header {
background: #001529;
color: white;
padding: 0 24px;
display: flex;
align-items: center;
}
.header h1 {
color: white;
margin: 0;
}
.content {
padding: 24px;
background: #f0f2f5;
}
.stats-row {
margin-bottom: 24px;
}
.stat-card {
text-align: center;
}
.stat-card h3 {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}
.stat-value {
font-size: 24px;
font-weight: bold;
margin: 10px 0;
color: #333;
}
.trend-up {
color: #52c41a;
font-size: 12px;
}
.trend-down {
color: #ff4d4f;
font-size: 12px;
}
</style>

5.4 代码解析

核心思路

  1. 布局设计:使用Ant Design Vue的Layout和Grid系统
  2. 关键指标:用Card组件展示统计数据
  3. 图表集成:使用G2Plot创建各种图表
  4. 响应式:使用Ant Design Vue的响应式栅格

关键点

  • 使用onMounted生命周期初始化图表
  • 图表容器需要指定ID和高度
  • 响应式布局适配不同屏幕

6. 选讲:可视化组件开发

6.1 封装图表组件

6.1.1 为什么需要封装?

问题:每个页面都要重复写图表初始化代码

解决方案:封装成可复用的组件


6.1.2 创建图表组件

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!-- components/ChartLine.vue -->
<template>
<div :id="chartId" :style="{ height: height + 'px' }"></div>
</template>

<script>
import { Line } from '@antv/g2plot';
import { onMounted, watch, onBeforeUnmount } from 'vue';

export default {
name: 'ChartLine',
props: {
data: {
type: Array,
required: true
},
xField: {
type: String,
default: 'x'
},
yField: {
type: String,
default: 'y'
},
height: {
type: Number,
default: 300
}
},
setup(props) {
const chartId = `line-chart-${Date.now()}`;
let chartInstance = null;

const initChart = () => {
if (chartInstance) {
chartInstance.destroy();
}

chartInstance = new Line(chartId, {
data: props.data,
xField: props.xField,
yField: props.yField
});

chartInstance.render();
};

onMounted(() => {
initChart();
});

// 监听数据变化,更新图表
watch(() => props.data, () => {
if (chartInstance) {
chartInstance.update({
data: props.data
});
}
}, { deep: true });

onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.destroy();
}
});

return {
chartId
};
}
};
</script>

使用方式

1
2
3
4
5
6
7
<template>
<ChartLine
:data="salesData"
x-field="month"
y-field="value"
:height="300" />
</template>


6.2 可视化设计原则

6.2.1 选择合适的图表类型

数据类型 推荐图表 说明
趋势数据 折线图 展示数据随时间的变化
对比数据 柱状图 对比不同类别的数值
占比数据 饼图 展示各部分占比
分布数据 散点图 展示数据分布情况

6.2.2 颜色使用原则

原则

  • 主色调统一:整个看板使用统一的配色方案
  • 对比明显:不同数据系列颜色要有区分度
  • 避免过多颜色:一般不超过5-7种颜色

实际例子

1
2
3
4
5
6
7
// 推荐:使用统一的颜色方案
const colors = ['#3498db', '#52c41a', '#ff4d4f', '#faad14'];

const chart = new Line('chart', {
data,
color: colors[0] // 使用统一的颜色
});


6.2.3 数据展示策略

原则

  • 突出重点:重要数据用大字体、醒目颜色
  • 层次清晰:标题、数据、说明层次分明
  • 信息适量:不要一次展示太多信息

实际例子

1
2
3
4
5
6
<!-- 好的设计:层次清晰 -->
<a-card>
<h3>总销售额</h3> <!-- 标题 -->
<p class="stat-value">¥100,000</p> <!-- 主要数据 -->
<span class="trend">↑ 12.5%</span> <!-- 辅助信息 -->
</a-card>


6.3 G2与G2Plot的关系

G2 = 底层的图形语法库

  • 特点:更灵活,可以自定义任何图表
  • 适用:需要高度自定义的场景
  • 难度:学习曲线较陡

G2Plot = 基于G2的高级封装

  • 特点:简单易用,开箱即用
  • 适用:常见的业务图表场景
  • 难度:学习曲线平缓

关系

1
2
3
4
5
G2(底层,灵活但复杂)

G2Plot(高级封装,简单易用)

Ant Design Vue(UI组件库集成)

💡 建议
对于初学者和大多数业务场景,使用G2Plot就足够了。
只有在需要高度自定义图表时,才考虑使用G2。


本章总结

通过本章的学习,你已经掌握了:

图表库使用

  • G2Plot的基础使用
  • 折线图、柱状图、饼图的创建
  • 图表配置选项

数据看板开发

  • 使用Ant Design Vue布局
  • 关键指标卡片展示
  • 多图表组合

动态数据更新

  • 实时更新图表数据
  • 定时刷新机制
  • 响应式调整

实战技能

  • 构建完整的数据看板
  • 使用AI生成代码
  • 封装图表组件

进阶知识(选讲):

  • 可视化设计原则
  • 图表组件封装
  • G2与G2Plot的关系

🌟 恭喜你!
你已经掌握了数据可视化的核心技能!
这些知识将帮助你:

  • 实现数据统计和展示

  • 构建专业的数据看板

  • 提升用户体验

继续练习,继续探索,数据可视化的世界等着你! 🚀