一、前言:图表≠可视化系统
广告平台的数据大屏最初是“页面堆图表”:
- 逻辑重复、样式不一致
- 图表配置散落页面中
- 接口调用、loading 控制混乱
我主导将其重构为:组件可复用 + 数据逻辑统一 + 可配置驱动的可视化系统。
二、系统结构全景图
+--------------------------+
| 页面/指标卡入口 |
+--------------------------+
↓ (配置传入)
+--------------------------+
| 图表组件 ChartCard | ➜ 图表渲染/响应式布局
+--------------------------+
↓ (数据拉取)
+--------------------------+
| 指标数据抽象层 Metric | ➜ API / 转换 / 状态
+--------------------------+
三、图表组件封装设计
统一封装 ChartCard.vue:支持 line / bar / pie 三种类型
<ChartCard
type="line"
title="点击量趋势"
:api="getClickData"
:params="{ dateType: '7d' }"
/>
支持:
- loading 内置控制
- 数据自动格式化映射
- 响应式 layout 支持
四、图表组件实现结构
<script setup>
const props = defineProps<{ type: 'line' | 'bar' | 'pie'; api: Function; params?: object }>()
const loading = ref(false)
const data = ref([])
onMounted(async () => {
loading.value = true
data.value = await props.api(props.params)
loading.value = false
})
const chartOption = computed(() => buildOption(props.type, data.value))
</script>
<template>
<n-card :title="title">
<v-chart :option="chartOption" v-show="!loading" />
<n-skeleton v-if="loading" />
</n-card>
</template>
五、指标数据抽象层设计(Metric)
所有接口封装为 metric:
export const getClickMetric = (params) => {
return request.get('/api/report/click', { params })
.then(res => res.list.map(item => ({ x: item.date, y: item.count })))
}
接口 → 映射函数 → 图表组件
支持统一格式 { x, y },方便组件解耦。
六、配置驱动页面搭建
页面只需配置指标 JSON:
const metricConfigs = [
{ title: '曝光量', type: 'line', api: getImpMetric },
{ title: '点击率', type: 'bar', api: getCTRMetric },
{ title: '消耗占比', type: 'pie', api: getCostRatioMetric }
]
<ChartCard v-for="m in metricConfigs" :key="m.title" v-bind="m" />
七、样式适配与响应式布局
- 每个 ChartCard 支持固定宽度或百分比布局
- 内部使用
ResizeObserver实现响应式调整 - 多列布局使用
n-grid+n-gi
八、性能优化策略
- 使用
svg模式初始化图表(ECharts) - 延迟渲染不可见图表(IntersectionObserver)
- 图表实例缓存 + 手动销毁(防止内存泄露)
九、测试与运维支持
- 指标接口支持 mock(支持历史模拟数据回放)
- 图表组件单测(props、loading、data 映射)
- 报错统一接入 sentry(包括图表渲染异常)
十、总结
真正的“数据可视化系统”不仅仅是展示图表,而是从接口抽象 → 数据结构设计 → 图表通用化封装 → 页面配置化落地的一整套工程流程。
我主导的这一套架构,已支持广告平台十余类业务数据模块统一接入,也为今后数据产品化打下基础。