收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南

作者:Rise Hao,云智慧前端开发工程师。开源项目数据可视化平台 FlyFish Maintainer。主攻可视化大屏方向,专注工程研发的降本增质、增效,在可视化方面具有丰富的开发经验 。

FlyFish 是云智慧公司自主设计、研发的一款低门槛、高拓展性的低代码应用开发平台, 为数据可视化开发场景提供了高效的一站式解决方案。FlyFish提供丰富的组件和应用模板库, 可通过拖拉拽的形式完成数据可视化开发,零开发背景的用户也可完成数据可视化开发工作。 同时,FlyFish也提供了灵活的拓展能力,支持组件开发、自定义函数与全局事件等配置, 面向复杂需求场景能够保证高效开发与交付。

相关文档地址

开始前(准备)

  1. FlyFish平台在线地址

  2. 创建项目(整体项目名称)

    • 查看是否有当前正要做的项目

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第1张图片

    • 添加当前项目(如果没有当前项目,有则忽略)

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第2张图片

    • 添加应用(可视化大屏)

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第3张图片

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第4张图片

    • 浏览是否有满足UI设计的基础组件(UI:可视化大屏组件样式)

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第5张图片

开始上手(初级)

  1. 选择要开发的应用(可视化大屏)

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第6张图片

  2. 选择适合的基础组件

    • 拖入可视化大屏内需要摆放的位置,去选择合适的配置满足UI的需求

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第7张图片

    • 如果仅通过配置项无法满足当前组件与UI的要求,可自定义CSS,添加css名字(会添加到当前组件的最外层,并在全局样式内进行自定义)

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第8张图片

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第9张图片

    • 请求数据的方式

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第10张图片

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第11张图片

  3. 选择当前项目下的组件(如果有的话...)

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第12张图片

开始开发(中级)

  1. 有类似的项目组件(但是仍需要进行定制化的)

    • 复制此组件,并起一个新的名字

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第13张图片

    • 编辑此组件信息,添加到当前项目

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第14张图片

  2. 添加定制化项目组件(如果基础组件不具备满足你当前的需求)

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第15张图片

  1. 项目组件开发,选择刚刚创建好的项目组件、点击开发组件

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第16张图片

  1. 代码结构

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第17张图片

build/webpack.config.dev.js

组件开发阶段保存对组件进行 webpack 编译打包扩展配置文件,具体请参考更改组件编译配置

#build/webpack.config.production.js

组件导出阶段对组件进行 webpack 编译打包扩展配置文件,具体请参考更改组件编译配置

#package.json

组件信息和依赖,具体请参考添加组件依赖

#options.json

组件开发底部的组件预览大屏的预设,具体请参考增加组件开发大屏预设

#src/main.js

组件注册入口,组件开发会自动产生此文件,如务必要不需要更改。具体请参考注册组件

#src/Component.js

组件代码文件,仅支持原生 Javascript 进行开发,请参考开发组件。如使用 react 开发,请参考React 开发组件

#src/setting.js

组件设置区域注册入口,组件开发会自动产生此文件,如组件有需要开发自定义配置和数据绑定,请打开此文件内注释掉的注册内容

#src/setting/options

组件设置区域组件,需使用 react 开发,具体请参考增加组件配置

#src/setting/data

组件设置数据区域组件,需使用 react 开发,具体请参考增加组件数据配置

  1. 是否需要配置模块

1 是右边的数据请求,2 是右边的模块配置

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第18张图片

  1. 数据请求方式(直接在代码中写)

    • 仅开发中生效--的模拟数据

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第19张图片

    • 大屏依然生效--的模拟数据(应用 = 可视化大屏)

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第20张图片

    • 默认选项,没有数据,但该参数又是必须参数(传递给组件的默认数据)

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第21张图片

  2. 组件内获取数据

    • 获取API请求数据,直接props中获取data

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第22张图片

    • 获取默认选项

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第23张图片

  3. 安装依赖(如果组件开发中需要引用某些插件)

FlyFish支持通过Echarts等外部平台开发组件,如有需要可通过引用相关插件的方式去实现。

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第24张图片

  1. 更新上线

收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第25张图片

开始进阶(高级)

  1. 设置大屏(官方文档

  2. 事件(官方文档

    • 组件之间传递事件

      1.     第一个箭头传递给某个组件事件以及参数
      2.     第二个箭头可以直接触发某个组件的数据请求

        收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第26张图片

    • 组件之间接收事件

      收到了组件传递过来的事件则会自动执行你的自定义

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第27张图片

  • 配置组件之间的事件(不配置不会生效哟)

    •   有了刚才组件的内部自定义的事件,我们可以设置之间的关联

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第28张图片

    •   如果选择红框框内的事件则作用在整个组件身上,如果选择红色箭头的事件则按照你刚才创建的方法开始执行

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第29张图片

    •   如果选择红色圆圈内的则作用于整个大屏之上,不在于某个组件内,如果选择红色方框则作用于所选的组件内部事件

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第30张图片

    •   选择刚刚定义的trigger事件

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第31张图片

    •   接收组件定义的方法(注意不是发送事件的那个哟,当然为了避免容易犯错误,你可以将两个名字设为一致)

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第32张图片

    •   可以选择修改刚刚定义的事件

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第33张图片

  1. 函数(官方文档

    自定义函数,常见的用法是提供给大屏的事件使用。

    收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第34张图片

  2. 全局数据集(官方文档

全局数据集可以给多个组件使用
收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第35张图片

开始进阶(骨灰级)

  1. 默认选项跟随数据进行实时渲染?

    • 重写load方法,因为他可以更新默认的选项 defaultOptions。

       /**
        * 加载数据
        * @param {Object=} options 临时加载选项
        * @param {function(Array.)=} onSuccess 加载完成回调
        * @param {function(string)} onError 加载失败回调
        * @returns {Component}
        */
       load(options = {}, onSuccess = null, onError = null) {
         if (this.hasDataSource()) {
           if (isFunction(options)) {
             /* eslint-disable no-param-reassign */
             onError = onSuccess;
             onSuccess = options;
             options = {};
             /* eslint-enable no-param-reassign */
           }
           // 加载数据事件
           this.trigger('load');
           this.dataSource.load(
             options,
             (data) => {
               call(onSuccess, this, data);
               let opt = this.getOptions()
               const { lineBackgroundDefault, lineBackground } = opt;
               const newLineBackground = data.dataList.map((_,i) => lineBackground[i] || lineBackgroundDefault);
               // 数据加载完成事件
               console.log(newLineBackground, '<--data')
               this.trigger('loaded', data);
               this.setOptions({lineBackground: JSON.parse(JSON.stringify(newLineBackground))})
               this.draw(data);
             },
             onError
           );
         }
         return this;
       }
      
      
    • 配置面板如何根据数据实现联动变化?

      • 在options.js文件写上下面的句子就可以拿到更新之后的数据了。todo(data)

        /*
         * @Author: Rise.Hao
         * @Date: 2022-05-11 22:53:50
         * @LastEditors: Rise.Hao
         * @LastEditTime: 2022-06-01 21:33:08
         * @Description: file content
         */
        
        'use strict';
        
        import React from 'react';
        import Base from './panel/index.js'
        import { cloneDeep } from "data-vi/helpers";
        import { recursionOptions } from '@cloudwise-fe/chart-panel'
        import { ComponentOptionsSetting } from 'datavi-editor/templates';
        export default class OptionsSetting extends ComponentOptionsSetting {
          constructor(props) {
         super(props)
          }
          // 可自定义样式: 若您在设置面板中书写样式会抽离出setting.css.
          // 显式的将以下属性设置为true可告知FlyFish来加载您的样式文件
          enableLoadCssFile = true;
        
          componentDidMount() {
         const { component } = this.props;
         component.bind('draw', () => {
           this.forceUpdate()
         })
          }
        
          componentWillUnmount() {
         const { component } = this.props;
         this.computedSettingStyleAppend(true);
         component.unbind('draw');
          }
        
          getTabs() {
         const options = recursionOptions(this.props.options, true)
         const {component, updateOptions} = this.props;
         return {
           config: {
             label: '配置',
             content: () => ,
           },
         }
          }
        }
        • 配置面板应该怎么写?
        /*
         * @Author: Rise.Hao
         * @Date: 2022-05-29 13:33:05
         * @LastEditors: Rise.Hao
         * @LastEditTime: 2022-06-01 22:00:47
         * @Description: file content
         */
        import React from 'react'
        import { Input, Select, ConfigProvider, InputNumber } from 'antd';
        import { ChartProvider, FormItem, FormItemGroup, CollapsePanel, Collapse, ColorPickerInput } from '@cloudwise-fe/chart-panel'
        export default function Index(props) {
          const { options, initialValues, onChange } = props;
          const { lineBackground = [] } = options || {};
        
          const lineFunc = (e, index, key) => {
         const newLineBackground = lineBackground.map((item, i) => {
           return i === index ? {
             ...item,
             [key]: e
           } : item
         })
         onChange({ lineBackground: newLineBackground })
          }
        console.log(lineBackground,'<--lineBackground')
          return 
         
           
             
               
                 {
                   lineBackground.map((item, index) => {
                     return 
                        lineFunc(e, index, 'background')} value={item.background} gradientMode="gradient" />
                     
                   })
                 }
               
             
             
               
                 
                   
                 
                 
                   
                 
               
             
           
         
          
        }
    • 有些时候更改了某个配置项而他有没有生效?

      比如:参数本身是一个数组又或者是一个对象,这个数组本身就存在,而你此次操作只是给数组里面删除了一个对象,最终没有生效。原因是FlyFish默认执行的setOptions是合并数据而不是更新数据

      • 把数组进行字符串处理,让他变成一个值,这样就不是合并了。
      • 重写setOptions方法,数组里面有的参数都执行更新操作,没有的执行合并操作。
    • import { defaultsDeep } from "data-vi/helpers";
      
      /**
           * 设置选项
           *
           * @param {Object} options 选项
           * @param {boolean} merge 是否合并原来的选项
           * @returns {Component}
           */
        setOptions(options = {}, merge = true) {
          const { replaceAll, ...mergeOptions } = options;
          const replaceKeys = ['lineBackground'];
          // 魔改一下部分结果处理
          if (replaceAll) {
            this.options = mergeOptions;
          } else if (merge) {
            let cloneOption = defaultsDeep({}, mergeOptions, this.options);
            if (replaceKeys.find((v) => typeof mergeOptions[v] !== 'undefined')) {
              cloneOption = {
                ...cloneOption,
                ...mergeOptions,
              };
            }
            this.options = cloneOption;
          } else {
            this.options = defaultsDeep({}, mergeOptions, this.getDefaultOptions());
          }
      1. 确保在所有组件加载完成后自动执行一个trigger方法?

      useEffect(() => {
          if (!nowdata) return;//nowdata是请求后端返回来的数据
          if (parent && parent.screen) {
            const allComponent = parent.screen.getComponents();
            const lastComponent = allComponent[allComponent.length - 1];
            if (lastComponent.mounted) {
              parent.trigger('add', { id: currentItem, value: nowdata })
            } else {
              lastComponent.bind("mounted", () => {
                parent.trigger('add', { id: currentItem, value: nowdata })
                lastComponent.unbind("mounted");
              })
            }
          }
        }, [nowdata])
      1. 我这个组件怎么去更改别的组件的默认选项?(谨慎操作)

      const compontentList = this.props.component.screen.getComponents()
      compontentList.forEach((item)=>{
      //这里可以做判断对那个组件进行操作
          item.setConfig({
            visible: true
          })
      }
      1. 建议不带 get 的 static?

       // 默认配置 static defaultConfig = {};
      getDefaultConfig() {
          return defaultsDeep({}, this.constructor.defaultConfig, {
              width: 100,
              height: 100,
              index: 0,
              left: 0,
              top: 0,
              name: '',
              visible: true,
              class: ''
          });
      }
      1. 输入框和FlyFish的事件冲突?

      // 禁止冒泡掉
      const bubblingFunc= (event)=>{
          event.stopPropagation();
      }
      
      1. 事件可以在组件里面直接写好了!

      // 注册事件
      registerComponentEvents("id", "DEFAULT_VERSION", {
          onChange: "变更",
          onValueChange: "值变更",
      });
      
      
      // 注册action
      registerComponentAction("id", "DEFAULT_VERSION", "changeValue", ReactCompont);
      
      call(component, "changeValue", ...args);
      
      // ReactCompont;
      export default (props) => (
          
      props.onChange(0, toNumber(event.target.value)) } /> props.onChange(1, toNumber(event.target.value)) } />
      );
      1. 静态文件从根目录取绝对路径的该如何设置?

      import { DEFAULT_VERSION } from "data-vi/components";
      import config from "data-vi/config";
      const componentStaticDir = props.parent.getVersion() == null || props.parent.getVersion() === DEFAULT_VERSION ? "components" : "release";
        const link = `${config.componentsDir}/${props.parent.getType()}/${props.parent.getVersion() || DEFAULT_VERSION}/${componentStaticDir}/public`;
        
        //webpack.config.production.js文件
        const CopyPlugin = require("copy-webpack-plugin");
        plugins: [
              new CopyPlugin( [
                  { from: path.resolve(__dirname, '../') + '/src/ModelRotates/public', to: path.resolve(__dirname, '../') + '/components/public/', },
              ]),
          ]
          
        //安装依赖
        "copy-webpack-plugin": "5.1.1"
      1. 组件内需要自己写请求?

      import { getHttpData } from 'data-vi/api';
      import { componentApiDomain } from 'data-vi/config';
      
      const getMapdata = (name) => {
          getHttpData(componentApiDomain + `/atlas/info?location=${encodeURIComponent(name)}`, 'GET', {})
            .done((request) => {
              console.log('请求成功', request)
              setNowdata(request.data)
            })
            .fail((request, xhr, msg) => {
              console.log('失败了')
            });
        }
      1. 比如跳转大屏如何根据url实现数据变更?

      function preDisposeParams(params) { 
          let sumParams = window.location.search ? window.location.search.split('?')[1] : '';
          let eachParams = sumParams.split('&')[1] || '';
          let systemCode = eachParams.split('=')[1] || '';
          let jsonParams ={
            "systemCode":systemCode
          }
          console.log(sumParams,"-",eachParams,"-",systemCode)
        return jsonParams; }
      1. 整张大屏内如何使用字体?【后期可能会更改】

        •   示例组件
      /**
           * 钩子方法 组件mount挂载时调用
           */
          _mount() {
            const container = this.getContainer();
            console.log(this.getType(),this.getVersion(),'123')
            const componentStaticDir =
            this.getVersion() == null || this.getVersion() === DEFAULT_VERSION ? "components" : "release";
            const link = `${config.componentsDir}/${this.getType()}/${
                this.getVersion() || DEFAULT_VERSION
            }/${componentStaticDir}/assets`;
            container.html(`
              
            `);
          }

      开发完成

      1. 点击预览,查看效果是否满足,并简单自测是否有BUG

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第36张图片

      1. 导出已完成的可视化大屏

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第37张图片

      部署上线

      1. componentApiDomain = 请求后端数据的ip地址

      2. 如果nginx代理没有从根目录配置则需要更改

        • iplpadImgDir的路径 = ./ (/data/app需要根据nginx代理的具体路径来配置)
        • components的路径= /data/app/components (/data/app需要根据nginx代理的具体路径来配置)

          收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第38张图片

      3. 标准流程 tengine部署

        • 修改前端部署包配置文件
        • 新建前端部署文件夹 web(/data/web/ tengine部署中都以该文件夹为例)
        • 将前端包文件screen.zip拷贝到该目录下
        • 解压命令:unzip screen.zip
        • 修改/data/app/sxdl_web/config/env.production.js 
        • 修改/data/tengine/conf/vhost/路径 (tengine部署目录为/data/tengine) 
        • 修改/data/app/sxdl_web/index.html (浏览器刷新文本) 
        • 修改/data/app/sxdl_web/config/env.conf.json (浏览器页签文本) 
        • 重启tengine服务 /data/app/tengine/sbin/nginx -s reload
        • 前端访问地址
      4. nginx部署

        • 修改前端部署目录xxxx/config/env.production.js配置文件
        • 配置文件: env.production.js:{ componentApiDomain:后端接口地址 } 
        • 部署环境nginx 注意:大屏前端配置的端口不可以和其他服务的前端的端口冲突
        • 首先备份/nginx/conf下的nginx.conf
        • 编辑nginx.conf

          重启ngnix /sbinx下 ./ngnix  -t                 //检查配置文件nginx.conf的正确性
          ./ngnix  -s  reload                                     //重新载入配置文件 
      5. 上传screen.zip file.dir: /var/www/html 将解压后screen.zip文件放入该目录(先清空html文件夹)

        备注:如果没有相同的路径,则随便找个路径放文件就行
      6. 解压screen.zip文件
      7. 修改/var/www/html/env.production.js文件 \修改配置文档: env.production.js
      8. 修改配置后,重启nginx
      9. 项目运行地址: 服务器地址+’/index.html’

        (注:前端本次用FlyFish开发页面,直接打出包,git上无仓库)

      下载源码

      1. 前缀:http://10.0.16.230:7001/appli...

        演示样例,真实链接会根据FlyFish本地的地址变化
      2. 大屏的ID

      收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南_第39张图片

      1. 最终下载地址(演示样例,非真实地址):http://10.0.16.230:7001/appli...

        注:前缀 + 大屏ID = 下载地址(请谨慎频繁调用)

        Echarts配置及导出(如有下载失败,请更换版本号):https://www.npmjs.com/package...

      2. 安装依赖: yarn 或 npm install
      3. 启动项目:yarn dev 或 npm run dev
      4. 再次编译:yarn build 或 npm run build

      你可能感兴趣的:(收藏!0 基础开源数据可视化平台 FlyFish 大屏开发指南)