数据可视化中图表的低无一体

介绍

数据可视化提供了自定义图表模板的功能,以满足现有图表模板无法满足业务需求的情况。

如何使用

  1. 点击数据可视化页面头部的“图表模板”
    image.png
  2. 点击创建按钮后弹出图表模板表单,填写后提交保存
    image.png
  3. 找到刚刚创建的图表模板,点击操作栏中的“低无一体”按钮
    image.png
  4. 点击弹窗底部的“生成sdk”按钮
    image.png
  5. 上一步操作完成后会重新刷新页面,再次找到该条数据,点击“低无一体”按钮,再次进来可以看到“下载模板工程”的按钮已经可以点击了,点击该按钮就可以下载到该自定义图表模板的示例代码包文件kunlun-chart-sdk.zip
    image.png
  6. 解压kunlun-chart-sdk.zip后我们可以看到工程结构,在根目录下执行npm i安装依赖
    image.png
  7. 可以看到packages/kunlun-plugin/src/chart/CUSTOM_BAR.vue是我们自定义的图表vue组件,可以修改里面的自定义展示和逻辑处理
  8. 完成自定义代码后去根目录运行npm run build打包代码,打包后可以在packages/kunlun-plugin/dist下看到打包后的js和css文件(如模板中无css则不会生成css文件,无需理会)
    image.png
  9. 回到自定义图表模板的管理页面,找到对应的数据行,再次点击“低无一体”按钮,在弹窗内上传上一步生成的kunlun-plugin.umd.jskunlun-plugin.css文件,上传完成后点击弹窗底部的“确定”按钮保存
    image.png
  10. 进入图表的编辑页面,在图表分类选择处,可以看到柱状图的分类下有我们新增的“自定义柱状图”的子类,点击切换
    image.png
  11. 切换后可以看到图表已经从开始柱状图变成了我们在前面自定义的蓝色边框样式,内部是hello chart文字的图表。
    image.png

示例自定义图表组件

本例子以echarts的图表库实现柱状图,框架自带的是以G2的库实现的柱状图
demo-echarts-bar.vue

<template>
  <div class="data-designer-chart-instance demo-echarts-bar" ref="designerChartViewRef">
    <div class="data-designer-chart-container" ref="designerChartViewInnerRef"></div>
  </div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, watch } from 'vue';
import DataSet from '@antv/data-set';

import * as echarts from 'echarts/core';
import { ECharts, EChartsCoreOption } from 'echarts/core';
import { GridComponent } from 'echarts/components';
import { BarChart, BarSeriesOption } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { deepClone } from '@kunlun/dependencies';
import {
  filterDimensionScaleColumns,
  isSameObj,
  ChartTypeEnum,
  IChartData,
  IChartDataResult,
  chartViewMixin,
  isNeedRerenderChart,
  isShowChatView,
  watchEchartsSize,
  ChartRenderEngine
} from '@kunlun/data-designer-core';

echarts.use([GridComponent, BarChart, CanvasRenderer]);

export default defineComponent({
  props: {
    ...chartViewMixin.props
  },
  data() {
    return {
      engine: ChartRenderEngine.ECHARTS,
      chartType: [ChartTypeEnum.MAP_CHINA]
    };
  },
  mixins: [chartViewMixin],
  setup(props, { emit }) {
    const chart = ref<ECharts>();
    const designerChartViewRef = ref<HTMLElement>(null as any);
    const designerChartViewInnerRef = ref<HTMLElement>(null as any);
    onMounted(() => {
      initChart();
    });
    let option = {} as EChartsCoreOption;

    function initChart() {
      chart.value = echarts.init(designerChartViewInnerRef.value);
      option = {
        yAxis: {
          type: 'value'
        }
      };
    }

    let oldChartData = {} as IChartData;
    watch(
      () => props.chartData,
      (newVal) => {
        if (!newVal || !chart.value) {
          return;
        }
        if (!isNeedRerenderChart(newVal, oldChartData)) {
          oldChartData = deepClone(newVal);
          return;
        }
        render(chart.value!, newVal, props.chartDataResult);
        oldChartData = deepClone(newVal);
      },
      {
        immediate: true,
        deep: true
      }
    );
    let oldChartDataResult = {} as IChartDataResult;

    function watchDataList(chartData: IChartData, chartDataResult: IChartDataResult) {
      oldChartDataResult = deepClone(props.chartDataResult);
      if (!chart.value) {
        initChart();
      }
      render(chart.value!, chartData, chartDataResult);
    }

    // 监听数据的变动自动重新渲染
    watch(
      () => props.chartDataResult.data,
      () => {
        if (isSameObj(oldChartDataResult, props.chartDataResult)) {
          return;
        }
        if (!designerChartViewRef.value) {
          onMounted(() => {
            watchDataList(props.chartData, props.chartDataResult);
          });
        } else {
          watchDataList(props.chartData, props.chartDataResult);
        }
      },
      { immediate: true, deep: true }
    );

    /**
     * 自定义渲染逻辑
     * @param chart echarts图表对象
     * @param chartData 图表模板的定义
     * @param chartDataResult chartDataResult.data存放的是后端返回的图表数据
     */
    function render(chart: ECharts, chartData: IChartData, chartDataResult: IChartDataResult) {
      if (!isShowChatView(chartData)) {
        return;
      }
      if (!isSameObj(chartData, chartDataResult.chartData)) {
        return;
      }

      const {
        scales = [],
        dimensions = [],
      } = filterDimensionScaleColumns(chartData);

      const dataList = !scales.length || !dimensions.length ? [] : ((chartDataResult.data! || []) as any[]);

      dataList.forEach((a) => {
        if (dimensions.length && !a.name) {
          a.name = a[dimensions[0].chartField.displayName];
        }
        if (scales.length && !a.value) {
          a.value = a[scales[0].chartField.displayName] || null;
        }
      });
      const dv = new DataSet.DataView().source(dataList);

      option.xAxis = {
        type: 'category',
        data: dv.rows?.map((a) => a?.name)
      };
      option.series = [
        {
          type: 'bar',
          data: dv.rows?.map((a) => a?.value)
        } as BarSeriesOption
      ];
      chart.setOption(option);
    }

    // 监听图表容器大小
    watchEchartsSize(props, chart!);

    return { designerChartViewRef, designerChartViewInnerRef, chart };
  }
});
</script>
注册该模板
import { ChartRenderEngine, ChartRenderType, registerChartComponent } from '@kunlun/data-designer-core';
import component from './demo-echarts-bar.vue';

registerChartComponent({
  engine: ChartRenderEngine.ECHARTS,
  render: ChartRenderType.CANVAS,
  chartTemplateCode: 'test002'
},
  {
    component
  } as any
);
效果展示

数据可视化中图表的低无一体

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/designer/70.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
nation的头像nation数式员工
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 上下文在字段和动作中的应用

    上下文在字段和动作中的应用 在业务场景中,常常需要在打开弹窗或跳转到新页面时携带当前页面数据。此时,我们需要配置相关「动作」中的上下文信息。 在 oinone 平台中,上下文主要分为以下三种: activeRecord:当前视图数据 rootRecord:主视图数据 openerRecord:触发弹窗的对象 参考文档:oinone内的主视图数据和当前视图数据使用介绍 activeRecord 表示当前视图的数据。例如,若动作配置在表单上,则指代当前表单的数据;若配置在 o2m、m2m 字段表格上,则指代选中的行数据。 rootRecord 表示根视图的数据。若当前视图是表单页,则代表表单的数据;若为表格页,则代表表格的数据。 openerRecord 表示触发弹窗的对象。例如,在弹窗内的字段或动作中,可通过 openerRecord 获取触发弹窗的信息。 这三者均为对象 (Object) 类型。 界面设计器配置 在 o2m、m2m 表格字段弹窗中携带当前视图数据 假设我们设计了一个包含 o2m、m2m 表格字段的表单页面。打开相关弹窗时,需将表单中的 code 数据传递至弹窗中。 选择相应的「动作」,如创建或添加。在右侧属性面板底部找到「上下文」,添加格式为对象 {} 的上下文信息。 以键值对的格式添加上下文信息:{code: rootRecord.code}。 设计弹窗时,将 code 字段拖入弹窗中。 完成设计后保存并发布。 大家可以看到,上下文中的key是 code,但是value是rootRecord.code,这里取的是rootRecord而不是activeRecord,因为我们上面讲过如果当前动作配置在o2m、m2m的字段表格上面,那么activeRecord就是表格选中的行,我们现在要取的是表单上的code字段,所以需要用rootRecord。 注意点:key需要是提交模型【前端视图】存在的字段才能传递。

    2023年11月8日
    1.8K10
  • 创建与编辑一体化

    在业务操作中,用户通常期望能够在创建页面后立即进行编辑,以减少频繁切换页面的步骤。我们可以充分利用Oinone平台提供的创建与编辑一体化功能,使操作更加高效便捷。 通过拖拽实现表单页面设计 在界面设计器中,我们首先需要设计出对应的页面。完成页面设计后,将需要的动作拖入设计好的页面。这个动作的关键在于支持一个功能,即根据前端传入的数据是否包含id来判断是创建操作还是编辑操作。 动作的属性配置如下: 前端自定义动作 一旦页面配置完成,前端需要对这个动作进行自定义。以下是一个示例的代码: @SPI.ClassFactory( ActionWidget.Token({ actionType: [ActionType.Server], model: '模型', name: '动作的名称' }) ) export class CreateOrUpdateServerActionWidget extends ServerActionWidget { @Widget.Reactive() protected get updateData(): boolean { return true; } } 通过以上步骤,我们实现了一个更智能的操作流程,使用户能够在创建页面的同时进行即时的编辑,从而提高了整体操作效率。这种创建与编辑一体化的功能不仅使操作更加顺畅,同时也为用户提供了更灵活的工作流程。

    2023年11月21日
    1.3K00
  • 如何实现页面间的跳转

    介绍 在日常的业务中,我们经常需要在多个模型的页面之间跳转,例如从商品的行可以直接点击链接跳转到类目详情,还有查看该订单发起的售后单列表,这里将给大家展示如何在oinone中如何实现这些功能。 方法一、通过界面设计器的无代码能力配置 表格行跳转到表单页/详情页 拖入一个跳转动作到表格行,保存动作后,在左侧的动作属性面板底部有个请求配置,里面的上下文属性就是配置跳转参数的地方,点击添加按钮可以增加一行参数 点击添加按钮后,可以看到新增了一行,行内有2个输入框,左侧输入框为目标视图模型的字段,右侧输入框为当前视图模型的表达式 注意 表达式中activeRecord关键字代表当前行的数据对象 “上下文”相关知识点 当前页面的模型和跳转后的页面模型相同的情况下,会字段带上当前行数据的id作为路由参数 上下文是从当前页面跳转到下个页面带的自定义参数 上下文会作为跳转后的页面数据加载函数的入参,后端的该函数需要根据该条件查询到数据返回给前端,典型的例子就是编辑页,根据id查询对象的其他字段信息返回 跳转后页面的数据加载函数可以在动作场景的时候选择加载函数,也可以在页面的加载函数处设置 方法二、通过低代码方式在自定义代码中调用 oinone提供了内置函数executeViewAction实现该功能 import { DefaultComparisonOperator, executeViewAction, QueryExpression, RuntimeViewAction, ViewActionTarget, ViewType } from '@kunlun/dependencies'; export class JumpActionWidget { protected goToObjectView() { executeViewAction( { viewType: ViewType.Form, moduleName: 'resource', model: 'resource.ResourceCountry', name: 'redirectUpdatePage', target: ViewActionTarget.Router } as RuntimeViewAction, undefined, undefined, { // 此处为id参数,目前只有表单和详情页需要 id: '12223', // 此处为上下文参数,context内对象的key是目标页面需要传递的默认值 context: JSON.stringify({code: 'xxx'}), // 此处为跳转后左侧菜单展开选中的配置 menu: JSON.stringify({"selectedKeys":["国家"],"openKeys":["地址库","地区"]}) } ); } protected goToListView() { const searchConditions: QueryExpression[] = []; searchConditions.push({ leftValue: ['countryCode'], // 查询条件的字段 operator: DefaultComparisonOperator.EQUAL, right: 'CN' // 字段的值 }); executeViewAction( { viewType: ViewType.Table, moduleName: 'resource', model: 'resource.ResourceCity', name: 'resource#市', target: ViewActionTarget.OpenWindow } as RuntimeViewAction, undefined, undefined, { // searchConditions相当于domain,不会随页面搜索项重置动作一起被清空 searchConditions: encodeURIComponent(JSON.stringify(searchConditions)), // searchBody的字段会填充搜索区域的字段组件,会随页面搜索项重置动作一起被清空 searchBody: JSON.stringify({code: 'CN'}), menu: JSON.stringify({"selectedKeys":["国家"],"openKeys":["地址库","地区"]}) } ); } } 扩展知识点 为什么executeViewAction跳转到的新页面不是入参的moduleName属性对应的模块? 答:跳转后所在的模块优先级为: 第一个入参的resModuleName属性对应的模块 执行executeViewAction时所在的模块 第一个入参的moduleName属性对应的模块 如何快速获取选中菜单的值? 答:先通过页面菜单手动打开页面,然后在浏览器自带调试工具的控制台执行decodeURIComponent(location.href),其中的menu参数就是我们需要的值

    2024年5月13日
    2.0K00
  • 如何把平台功能菜单加到项目中?

    概述 在使用Oinone低代码平台进行项目开发的过程中,会存在把平台默认提供的菜单加到自己的项目中。这篇文章介绍实现方式,以角色管理为例。 1. 低代码情况 即项目是通过后端代码初始化菜单的方式。 通常在 XXXMenu.java类通过@UxMenu注解的方式,代码片段如下: 此种情况与添加项目本地的菜单无区别,具体代码: @UxMenu(value = "账号管理", icon = "icon-yonghuguanli") class AccountManage { @UxMenu("用户管理") @UxRoute(value = CustomerCompanyUserProxy.MODEL_MODEL, title = "用户管理") class CompanyUserManage { } @UxMenu("角色管理") @UxRoute(value = AuthRole.MODEL_MODEL, module = AdminModule.MODULE_MODULE, title = "角色管理") class RoleManage { } @UxMenu("操作日志") @UxRoute(value = OperationLog.MODEL_MODEL, module = AdminModule.MODULE_MODULE/***菜单所挂载的模块**/) class OperateLog { } } 2. 无代码情况 在界面设计器中,新建菜单–>选择绑定已有页面,进行发布即可。

    2024年4月19日
    1.5K00
  • 工作流动态表单使用说明

    需求背景 为了提高操作效率并简化流程设计过程,应对伙伴们反映的在流程设计器中,即使填写/审批流程相同,不同模型也需重新配置的问题,我们引入了“动态表单”功能。此功能旨在减少重复配置的需求,通过设置节点名称和绑定视图,便可实现审批流程相同而视图不同,从而使得相同的审批流程可以被高效重复利用。 核心功能是:通过自定义函数实现视图的动态渲染,如图所示《动态表单函数》。 自定义动态表单函数 动态表单函数定义规则如下: namespace:强制为 WorkflowFunctionConstant.FUNCTION_NAMESPACE。 fun:强制以 WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX 为前缀。 入参说明: 参数1:节点数据,例如,配合instanceof可以判断当前是填写节点(WriteNode)还是审批节点(ApprovalNode)。 参数2:触发节点的模型数据,如果您的触发节点不确定,可以通过Map接收参数。 参数3:该节点所配置数据来源的数据。 出参说明:视图,如果出参为null,流程终止运行,错误信息提示为“流程节点执行失败,动态表单函数获取视图为空”。 @Slf4j @Component @Fun(WorkflowFunctionConstant.FUNCTION_NAMESPACE) public class DynamicFormCustom { /** * 根据动态表单任务获取视图 * * @param node 节点数据 * @param dynamicFormTask 触发节点数据 * @param dataObj 源数据 */ @Function.fun(WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX + "fetchDynamicFormFunction") @Function.Advanced(displayName = "[内置]获取动态表单函数") @Function(name = "fetchDynamicFormFunction") public View fetchDynamicFormFunction(Node node, DynamicFormTask dynamicFormTask, Map<String, Object> dataObj) { DynamicFormTaskNode dynamicFormTaskNode = fetchDynamicFormTaskNode(node, dynamicFormTask); if (dynamicFormTaskNode == null) { return null; } dynamicFormTaskNode.fieldQuery(DynamicFormTaskNode::getView); return dynamicFormTaskNode.getView(); } private DynamicFormTaskNode fetchDynamicFormTaskNode(Node node, DynamicFormTask dynamicFormTask) { List<DynamicFormTaskNode> dynamicFormTaskNodeList = Models.origin().queryListByWrapper(Pops.<DynamicFormTaskNode>lambdaQuery() .from(DynamicFormTaskNode.MODEL_MODEL) .eq(DynamicFormTaskNode::getNodeName, node.getNodeName()) .eq(DynamicFormTaskNode::getTaskCode, dynamicFormTask.getCode()) ); if (CollectionUtils.isEmpty(dynamicFormTaskNodeList)) { return null; } if (dynamicFormTaskNodeList.size() > 1) { log.error("工作流动态获取表单函数视图匹配多个,{}", JsonUtils.toJSONString(dynamicFormTaskNodeList)); } return dynamicFormTaskNodeList.get(0); } } 相关链接 工作流动态表单最佳实践

    2025年2月7日
    74700

Leave a Reply

登录后才能评论