背景
在数据大屏中,图表可视化的样式、动画效果能够大大提升用户的视觉体验和交互感受。
VChart 的特效种类繁多,其适配的主题也涵盖了多种风格,从简约现代到复古经典,从科技感十足到自然生态,能够满足不同场景和用户需求。而且,VChart 的样式定制化极其灵活,用户可以根据自身品牌形象和数据特点,对图表的颜色、形状、大小等进行个性化调整,从而打造出独一无二的数据大屏展示。
VChart 已在多个 React 项目中集成并使用,但鲜有 Vue 框架的实践案例。为此我们基于热门开源大屏项目 Go-View 进行拓展,集成 VChart 可视化能力,丰富 Go-View 的功能和表现形式。
GO-View项目
GoView 是一个Vue3搭建的低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可完成业务需求。 它的技术栈为:Vue3 + TypeScript4 + Vite2 + NaiveUI + ECharts5 +VChart + Axios + Pinia2 + PlopJS。它提供拖拽式组件库与动态数据绑定能力,支持快速搭建交互式数据大屏。其核心功能涵盖多主题适配、实时数据渲染、企业级安全模块,适用于智慧城市、商业智能等场景。
📚 GoView 文档 地址:www.mtruning.club/
纯前端 Demo 地址:vue.mtruning.club/
带后端 Demo 地址:demo.mtruning.club/
GoView 源码地址:gitee.com/MTrun/go-vi...
功能实现
以新增一个柱状图组件为例
::: warning
务必进行的测试:
- 右侧属性响应式测试
 - 数据->动态数据更新测试
 - 使用 mock 数据,更新时间设置为 5S,点击预览查看 5S 后图表是否进行了更新
 - 打包编译测试,编译会报 TS 错误,请及时修改.注意: 开发环境并未做 tree-shaking 处理,以下按需导入等同于 import VChart from '@visactor/vchart'全量导入。但当打包编译时将会开启 tree-shaking,如缺少必要组件导入,项目运行将会报错!注册逻辑参考: www.visactor.io/vchart/guid...
 
:::
- 首先在 
packages的文件夹里新增基础配置文件 
| 路径 | 功能 | 
|---|---|
| packages/index | 所有图表导出,图表动态加载方法等 | 
| packages/index.d | 类型定义 | 
| packages/public | 公共数据类,方法等 | 
| packages/chartConfiguration | 图表基础配置 | 
| packages/components/Charts | 图表模块 | 
| packages/components/VCharts | VChart 图表模块 | 
| packages/components/Informations | 信息模块 | 
| packages/components/Tables | 表格模块 | 
| packages/components/Decorates | 装饰模块 | 
选择在 packages/components/VChart/Bars 下创建 VChartBarCommon 文件夹
- 在文件夹内创建对应的文件 
index.ts、index.vue、config.ts、config.vue 
| 文件 | 功能 | 
|---|---|
| index.ts | 图表声明文件 | 
| index.vue | 展示渲染文件 | 
| config.ts | 数据相关文件 | 
| config.vue | 设置项内容 | 
| data.json | 静态数据(可无) | 
index.ts 内容如下:
            
            
              javascript
              
              
            
          
          // 公共类型声明
import {
  ConfigType,
  PackagesCategoryEnum,
  ChartFrameEnum,
} from "@/packages/index.d";
// 当前[信息模块]分类声明
import { ChatCategoryEnum, ChatCategoryEnumName } from "../../index.d";
export const VChartBarCommonConfig: ConfigType = {
  // 唯一key,注意!!!文件夹名称需要修改成与当前组件一致!!!
  key: "VChartBarCommon",
  // 图表组件渲染 Components 格式: V + key
  chartKey: "VVChartBarCommon",
  // 配置组件渲染 Components 格式: VC + key
  conKey: "VCVChartBarCommon",
  // 名称
  title: "VChart并列柱状图",
  // 子分类目录
  category: ChatCategoryEnum.BAR,
  // 子分类目录
  categoryName: ChatCategoryEnumName.BAR,
  // 包分类
  package: PackagesCategoryEnum.VCHART,
  chartFrame: ChartFrameEnum.VCHART,
  // 图片 (注意!图片存放的路径必须在 src/assets/images/chart/包分类名称/*)
  // 文件夹名称需要和包分类名称一致: PackagesCategoryEnum.VCHART
  image: "bar_x.png",
};
        :::warning 注意!
v1.1.9 / v2.1.6 版本以下,图片需要直接引入。但是开发环境生成的组件,在生产环境的层级展示中图片会有问题。
:::
使用方式如下:
            
            
              arduino
              
              
            
          
          // 展示图片
import image from "@/assets/images/chart/vchart/bar_x.png";
export const VChartBarCommonConfig: ConfigType = {
  // .....和上面一致
  // 图片
  image: image,
};
        data.json 内容如下:
            
            
              json
              
              
            
          
          {
  "values": [
    { "type": "Autocracies", "year": "1930", "value": 129 },
    { "type": "Autocracies", "year": "1940", "value": 133 }
    // ...
  ]
}
        config.ts 内容如下,在创建新图表时会执行 new Config()
            
            
              typescript
              
              
            
          
          // 公共类型和方法
import { vChartOptionPrefixHandle, PublicConfigClass } from "@/packages/public";
// 公共类型
import { CreateComponentType } from "@/packages/index.d";
// 获取上面的 index 配置内容
import { VChartBarCommonConfig } from "./index";
// 深拷贝
import cloneDeep from "lodash/cloneDeep";
// 默认数据
import data from "./data.json";
// 图表公共主题配置
import axisThemeJson from "@/settings/vchartThemes/axis.theme.json";
// 图表配置的类型定义
import { IBarOption } from "../../index.d";
// 从VCharts 的默认配置项里取出需要的部分,详见 `src/settings/chartThemes/index`
export const includes = ["legends", "tooltip"];
export const option = {
  // 图表配置
  type: "bar",
  dataset: data,
  stack: true,
  xField: ["year", "type"],
  yField: ["value"],
  seriesField: "type",
  // 业务配置(后续会被转换为图表spec)
  category: VChartBarCommonConfig.category,
  xAxis: {
    name: "x轴",
    ...axisThemeJson,
    grid: {
      ...axisThemeJson.grid,
      visible: false,
    },
  },
  yAxis: {
    name: "y轴",
    ...axisThemeJson,
    grid: {
      ...axisThemeJson.grid,
      style: {
        ...axisThemeJson.grid.style,
        lineDash: [3, 3],
      },
    },
  },
};
// 柱状图类
export default class Config
  extends PublicConfigClass
  implements CreateComponentType
{
  public key = VChartBarCommonConfig.key;
  public chartConfig = cloneDeep(VChartBarCommonConfig);
  // 进行样式合并
  public option = vChartOptionPrefixHandle(option, includes);
}
        index.vue 内容如下:
            
            
              xml
              
              
            
          
          <template>
  <GoVChart ref="vChartRef" :option="chartConfig.option"> </GoVChart>
</template>
<script setup lang="ts">
import { PropType } from "vue";
import { useChartEditStore } from "@/store/modules/chartEditStore/chartEditStore";
import { GoVChart } from "@/components/GoVChart";
import { useChartDataFetch } from "@/hooks";
import config from "./config";
const props = defineProps({
  chartConfig: {
    type: Object as PropType<config>,
    required: true,
  },
});
const { vChartRef } = useChartDataFetch(
  props.chartConfig,
  useChartEditStore,
  (newData: any) => {
    props.chartConfig.option.dataset = newData;
  }
);
</script>
        config.vue 内容如下:
            
            
              xml
              
              
            
          
          <template>
  <!-- vCharts 全局设置 -->
  <VChartGlobalSetting :optionData="optionData"></VChartGlobalSetting>
  <Axis :axis="optionData.xAxis"></Axis>
  <Axis :axis="optionData.yAxis"></Axis>
</template>
<script setup lang="ts">
import { PropType } from "vue";
import {
  VChartGlobalSetting,
  Axis,
} from "@/components/Pages/VChartItemSetting";
import { vChartGlobalThemeJsonType } from "@/settings/vchartThemes/index";
defineProps({
  optionData: {
    type: Object as PropType<vChartGlobalThemeJsonType>,
    required: true,
  },
});
</script>
        - 然后把图表组件在 
src\packages\components\VChart\Bars\index.ts中进行导出 
            
            
              javascript
              
              
            
          
          import { VChartBarCommonConfig } from "./VChartBarCommon/index";
import { VChartBarStackConfig } from "./VChartBarStack/index";
// 这里的顺序决定着最终的展示顺序
export default [VChartBarCommonConfig, VChartBarStackConfig];
        注意在 src\packages\components\VChart\index.ts 也有一个导出,这里是导出 VChart 模块的所有组件
            
            
              javascript
              
              
            
          
          import Bars from "./Bars";
export const ChartList = [...Bars];
        - 在
src\components\GoVChart\transformProps\bars.ts将业务配置转换为可供 VChart 绘制的标准 spec 
            
            
              ini
              
              
            
          
          import { cloneDeep } from "lodash";
export default (chartProps: any) => {
  // 将业务配置拷贝出来处理, 避免影响原配置
  const spec = cloneDeep(chartProps);
  // 图表类型仅供索引到对应图表spec转换逻辑使用,。在VChart配置中, type即可识别图表类型
  delete spec.category;
  // tooltip spec 转换
  const keyFill = spec.tooltip.style.keyLabel.fill;
  const valueFill = spec.tooltip.style.valueLabel.fill;
  const titleFill = spec.tooltip.style.titleLabel.keyFill;
  delete spec.tooltip.style.keyLabel.fill;
  delete spec.tooltip.style.valueLabel.fill;
  delete spec.tooltip.style.titleLabel.keyFill;
  spec.tooltip.style.keyLabel.fontColor = keyFill;
  spec.tooltip.style.valueLabel.fontColor = valueFill;
  spec.tooltip.style.titleLabel.fontColor = titleFill;
  // 轴spec 转换
  const { name: xAxisName, ...restXAxisProps } = chartProps.xAxis;
  const { name: yAxisName, ...restYAxisProps } = chartProps.yAxis;
  spec.axes = [
    {
      orient: "bottom",
      ...restXAxisProps,
      // paddingInner: 0.5
    },
    {
      orient: "left",
      ...restYAxisProps,
    },
  ];
  // 去掉无用业务配置
  delete spec.xAxis;
  delete spec.yAxis;
  // console.log('spec-bar-transform', spec)
  // 返回VChart可识别的标准spec
  return spec;
};
        - 在
src\components\GoVChart\transformProps\index.ts进行导出 
            
            
              typescript
              
              
            
          
          import {
  ChatCategoryEnum,
  IOption,
} from "@/packages/components/VChart/index.d";
import bars from "./bars";
export const transformHandler: {
  [key: string]: (args: IOption) => any;
} = {
  // 业务配置中的category用于索引到对应的转换逻辑
  [ChatCategoryEnum.BAR]: bars,
  // todo: more charts handler
};
        - 
在
src/components/GoVChart/register.ts中执行图表注册逻辑. - 
开发环境并未做 tree-shaking 处理,以下按需导入等同于
import VChart from '@visactor/vchart'全量导入。但需注意,当打包编译时将会开启 tree-shaking,如缺少必要组件导入,项目运行将会报错! 
            
            
              javascript
              
              
            
          
          import { VChart } from "@visactor/vchart/esm/core";
import { registerBarChart } from "@visactor/vchart/esm/chart";
import {
  registerTooltip,
  registerCartesianCrossHair,
  registerDiscreteLegend,
  registerLabel,
} from "@visactor/vchart/esm/component";
import { registerDomTooltipHandler } from "@visactor/vchart/esm/plugin/components";
import { registerAnimate } from "@visactor/vchart";
// 不同图表类型需要注册什么样的组件可以参考: https://www.visactor.io/vchart/guide/tutorial_docs/Load_on_Demand
export const registerChartsAndComponents = () => {
  VChart.useRegisters([
    // 图表
    registerBarChart,
    // 组件
    registerTooltip,
    registerDomTooltipHandler,
    registerCartesianCrossHair,
    registerDiscreteLegend,
    registerLabel,
    // 动画
    registerAnimate,
  ]);
};
        此时页面图表中将出现【柱状图】组件,试试把它拖拽到页面进行测试吧~
最终效果
欢迎交流
最后,我们诚挚的欢迎所有对数据可视化感兴趣的朋友参与进来,参与 VisActor 的开源建设:
VChart :VChart 官网、VChart Github(欢迎 Star)
VTable :VTable 官网、VTable Github(欢迎 Star)
VMind :VMind 官网、VMind Github(欢迎 Star)
官方网站:www.visactor.io/ 或 www.viactor.com
Discord:discord.gg/3wPyxVyH6m
飞书群(外网):打开链接扫码
微信公众号:打开链接扫码
github:github.com/VisActor