作者:RiseHao,云智慧前端开发工程师。开源项目数据可视化平台FlyFishMaintainer。主攻可视化大屏方向,专注工程研发的降本增质、增效,在可视化方面具有丰富的开发经验。 FlyFish是云智慧公司自主设计、研发的一款低门槛、高拓展性的低代码应用开发平台,为数据可视化开发场景提供了高效的一站式解决方案。FlyFish提供丰富的组件和应用模板库,可通过拖拉拽的形式完成数据可视化开发,零开发背景的用户也可完成数据可视化开发工作。同时,FlyFish也提供了灵活的拓展能力,支持组件开发、自定义函数与全局事件等配置,面向复杂需求场景能够保证高效开发与交付。相关文档地址FlyFish官方开发文档:http:docs。aiops。cloudwise。comzhflyfishgettingstartedYAPI数据模拟文档:https:hellosean1025。github。ioyapidocumentsadvmock。htmlGitHub地址:https:github。comCloudWiseOpenSourceFlyFishGitee地址:https:gitee。comCloudWiseflyfish开始前(准备)FlyFish平台在线地址创建项目(整体项目名称)查看是否有当前正要做的项目添加当前项目(如果没有当前项目,有则忽略)添加应用(可视化大屏)浏览是否有满足UI设计的基础组件(UI:可视化大屏组件样式)开始上手(初级)选择要开发的应用(可视化大屏) 选择适合的基础组件拖入可视化大屏内需要摆放的位置,去选择合适的配置满足UI的需求,如果仅通过配置项无法满足当前组件与UI的要求,可自定义CSS,添加css名字(会添加到当前组件的最外层,并在全局样式内进行自定义)请求数据的方式选择当前项目下的组件(如果有的话。。。) 开始开发(中级)有类似的项目组件(但是仍需要进行定制化的)复制此组件,并起一个新的名字编辑此组件信息,添加到当前项目添加定制化项目组件(如果基础组件不具备满足你当前的需求) 项目组件开发,选择刚刚创建好的项目组件、点击开发组件 代码结构 buildwebpack。config。dev。js 组件开发阶段保存对组件进行webpack编译打包扩展配置文件,具体请参考更改组件编译配置 buildwebpack。config。production。js 组件导出阶段对组件进行webpack编译打包扩展配置文件,具体请参考更改组件编译配置 package。json 组件信息和依赖,具体请参考添加组件依赖 options。json 组件开发底部的组件预览大屏的预设,具体请参考增加组件开发大屏预设 srcmain。js 组件注册入口,组件开发会自动产生此文件,如务必要不需要更改。具体请参考注册组件 srcComponent。js 组件代码文件,仅支持原生Javascript进行开发,请参考开发组件。如使用react开发,请参考React开发组件 srcsetting。js 组件设置区域注册入口,组件开发会自动产生此文件,如组件有需要开发自定义配置和数据绑定,请打开此文件内注释掉的注册内容 srcsettingoptions 组件设置区域组件,需使用react开发,具体请参考增加组件配置 srcsettingdata 组件设置数据区域组件,需使用react开发,具体请参考增加组件数据配置是否需要配置模块 1是右边的数据请求,2是右边的模块配置 数据请求方式(直接在代码中写)仅开发中生效的模拟数据大屏依然生效的模拟数据(应用可视化大屏)默认选项,没有数据,但该参数又是必须参数(传递给组件的默认数据)组件内获取数据获取API请求数据,直接props中获取data获取默认选项安装依赖(如果组件开发中需要引用某些插件) FlyFish支持通过Echarts等外部平台开发组件,如有需要可通过引用相关插件的方式去实现。 更新上线 开始进阶(高级)设置大屏事件 组件之间传递事件第一个箭头传递给某个组件事件以及参数第二个箭头可以直接触发某个组件的数据请求组件之间接收事件收到了组件传递过来的事件则会自动执行你的自定义配置组件之间的事件(不配置不会生效哟)有了刚才组件的内部自定义的事件,我们可以设置之间的关联如果选择红框框内的事件则作用在整个组件身上,如果选择红色箭头的事件则按照你刚才创建的方法开始执行如果选择红色圆圈内的则作用于整个大屏之上,不在于某个组件内,如果选择红色方框则作用于所选的组件内部事件选择刚刚定义的trigger事件接收组件定义的方法(注意不是发送事件的那个哟,当然为了避免容易犯错误,你可以将两个名字设为一致)可以选择修改刚刚定义的事件函数 自定义函数,常见的用法是提供给大屏的事件使用。 全局数据集(官方文档)全局数据集可以给多个组件使用开始进阶(骨灰级)默认选项跟随数据进行实时渲染? 重写load方法,因为他可以更新默认的选项defaultOptions。加载数据param{Object}options临时加载选项param{function(Array。Object)}onSuccess加载完成回调param{function(string)}onError加载失败回调returns{Component}load(options{},onSuccessnull,onErrornull){if(this。hasDataSource()){if(isFunction(options)){eslintdisablenoparamreassignonErroronSonSoptions{};eslintenablenoparamreassign}加载数据事件this。trigger(load);this。dataSource。load(options,(data){call(onSuccess,this,data);letoptthis。getOptions()const{lineBackgroundDefault,lineBackground}constnewLineBackgrounddata。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);}} 配置面板如何根据数据实现联动变化? 在options。js文件写上下面的句子就可以拿到更新之后的数据了。Author:Rise。HaoDate:2022051122:53:50LastEditors:Rise。HaoLastEditTime:2022060121:33:08Description:importRimportBasefrom。panelindex。jsimport{cloneDeep}import{recursionOptions}fromcloudwisefechartpanelimport{ComponentOptionsSetting}exportdefaultclassOptionsSettingextendsComponentOptionsSetting{constructor(props){super(props)}可自定义样式:若您在设置面板中书写样式会抽离出setting。css。显式的将以下属性设置为true可告知FlyFish来加载您的样式文件enableLoadCssFcomponentDidMount(){const{component}this。component。bind(draw,(){this。forceUpdate()})}componentWillUnmount(){const{component}this。this。computedSettingStyleAppend(true);component。unbind(draw);}getTabs(){constoptionsrecursionOptions(this。props。options,true)const{component,updateOptions}this。return{config:{label:配置,content:()BaseinitialValues{options}props{this。props}options{cloneDeep(component。getOptions())}onChange{updateOptions},},}}}有些时候更改了某个配置项而他有没有生效? 比如:参数本身是一个数组又或者是一个对象,这个数组本身就存在,而你此次操作只是给数组里面删除了一个对象,最终没有生效。原因是FlyFish默认执行的setOptions是合并数据而不是更新数据把数组进行字符串处理,让他变成一个值,这样就不是合并了。重写setOptions方法,数组里面有的参数都执行更新操作,没有的执行合并操作。import{defaultsDeep}设置选项param{Object}options选项param{boolean}merge是否合并原来的选项returns{Component}setOptions(options{},mergetrue){const{replaceAll,。。。mergeOptions}constreplaceKeys〔lineBackground〕;魔改一下部分结果处理if(replaceAll){this。optionsmergeO}elseif(merge){letcloneOptiondefaultsDeep({},mergeOptions,this。options);if(replaceKeys。find((v)typeofmergeOptions〔v〕!undefined)){cloneOption{。。。cloneOption,。。。mergeOptions,};}this。optionscloneO}else{this。optionsdefaultsDeep({},mergeOptions,this。getDefaultOptions());}确保在所有组件加载完成后自动执行一个trigger方法?useEffect((){if(!nowdata)nowdata是请求后端返回来的数据if(parentparent。screen){constallComponentparent。screen。getComponents();constlastComponentallComponent〔allComponent。length1〕;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〕)我这个组件怎么去更改别的组件的默认选项?(谨慎操作)constcompontentListthis。props。component。screen。getComponents()compontentList。forEach((item){这里可以做判断对那个组件进行操作item。setConfig({visible:true})}建议不带get的static?默认配置staticdefaultConfig{};getDefaultConfig(){returndefaultsDeep({},this。constructor。defaultConfig,{width:100,height:100,index:0,left:0,top:0,name:,visible:true,class:});}输入框和FlyFish的事件冲突?禁止冒泡掉constbubblingFunc(event){event。stopPropagation();}inputonKeyUp{bubblingFunc}onKeyDown{bubblingFunc}事件可以在组件里面直接写好了!注册事件registerComponentEvents(id,DEFAULTVERSION,{onChange:变更,onValueChange:值变更,});注册actionregisterComponentAction(id,DEFAULTVERSION,changeValue,ReactCompont);call(component,changeValue,。。。args);ReactCexportdefault(props)();静态文件从根目录取绝对路径的该如何设置?import{DEFAULTVERSION}constcomponentStaticDirprops。parent。getVersion()nullprops。parent。getVersion()DEFAULTVERSION?components:constlink{config。componentsDir}{props。parent。getType()}{props。parent。getVersion()DEFAULTVERSION}{componentStaticDir}webpack。config。production。js文件constCopyPluginrequire(copywebpackplugin);plugins:〔newCopyPlugin(〔{from:path。resolve(dirname,。。)srcModelRotatespublic,to:path。resolve(dirname,。。)componentspublic,},〕),〕安装依赖copywebpackplugin:5。1。1组件内需要自己写请求?import{getHttpData}import{componentApiDomain}constgetMapdata(name){getHttpData(componentApiDomainatlasinfo?location{encodeURIComponent(name)},GET,{})。done((request){console。log(请求成功,request)setNowdata(request。data)})。fail((request,xhr,msg){console。log(失败了)});}比如跳转大屏如何根据url实现数据变更?functionpreDisposeParams(params){letsumParamswindow。location。search?window。location。search。split(?)〔1〕:;leteachParamssumParams。split()〔1〕;letsystemCodeeachParams。split()〔1〕;letjsonParams{systemCode:systemCode}console。log(sumParams,,eachParams,,systemCode)returnjsonP}整张大屏内如何使用字体?【后期可能会更改】示例组件钩子方法组件mount挂载时调用mount(){constcontainerthis。getContainer();console。log(this。getType(),this。getVersion(),123)constcomponentStaticDirthis。getVersion()nullthis。getVersion()DEFAULTVERSION?components:constlink{config。componentsDir}{this。getType()}{this。getVersion()DEFAULTVERSION}{componentStaticDir}container。html(stylefontface{fontfamily:FZZYJW;src:url({link}FZZYJW。TTF);}fontface{fontfamily:FZZZHONGJW;src:url({link}FZZZHONGJW。TTF);}fontface{fontfamily:HYa9src:url({link}hya9gjm。ttf);}fontface{fontfamily:HYk2src:url({link}HYLingXin。ttf);}fontface{fontfamily:SourceHanSerifSCHsrc:url({link}SourceHanSerifSCHeavy。ttf);}style);}开发完成点击预览,查看效果是否满足,并简单自测是否有BUG 导出已完成的可视化大屏 部署上线componentApiDomain请求后端数据的ip地址如果nginx代理没有从根目录配置则需要更改iplpadImgDir的路径。(dataapp需要根据nginx代理的具体路径来配置)components的路径dataappcomponents(dataapp需要根据nginx代理的具体路径来配置) 标准流程tengine部署修改前端部署包配置文件新建前端部署文件夹web(datawebtengine部署中都以该文件夹为例)将前端包文件screen。zip拷贝到该目录下解压命令:unzipscreen。zip修改dataappsxdlwebconfigenv。production。js修改datatengineconfvhost路径(tengine部署目录为datatengine)修改dataappsxdlwebindex。html(浏览器刷新文本)修改dataappsxdlwebconfigenv。conf。json(浏览器页签文本)重启tengine服务dataapptenginesbinnginxsreload前端访问地址nginx部署修改前端部署目录xxxxconfigenv。production。js配置文件配置文件:env。production。js:{componentApiDomain:后端接口地址}部署环境nginx注意:大屏前端配置的端口不可以和其他服务的前端的端口冲突首先备份nginxconf下的nginx。conf编辑nginx。conf重启ngnixsbinx下。ngnixt检查配置文件nginx。conf的正确性。ngnixsreload重新载入配置文件上传screen。zipfile。dir:varwwwhtml将解压后screen。zip文件放入该目录(先清空html文件夹)备注:如果没有相同的路径,则随便找个路径放文件就行解压screen。zip文件修改varwwwhtmlenv。production。js文件修改配置文档:env。production。js修改配置后,重启nginx项目运行地址:服务器地址’index。html’ 前端本次用FlyFish开发页面,直接打出包,git上无仓库下载源码前缀:http:10。0。16。230:7001applicationsexportsource 演示样例,真实链接会根据FlyFish本地的地址变化大屏的ID 最终下载地址(演示样例,非真实地址):http:10。0。16。230:7001applicationsexportsource62134fbaddc0c8314cd3be30注:前缀大屏ID下载地址(请谨慎频繁调用) Echarts配置及导出:https:www。npmjs。compackagecloudwisefechartpanel(如有下载失败,请更换版本号)安装依赖:yarn或npminstall启动项目:yarndev或npmrundev再次编译:yarnbuild或npmrunbuild