【仅供内部供应商使用,不提供对外解答和培训】
【仅供内部供应商使用,不提供对外解答和培训】
图表插件是用于丰富帆软的图表类型,旨在帮助用户更好地做数据可视化展现。开发者需要通过帆软开放的接口,参考下面的demo和文档来制作插件。
我们建议您在上手开发插件之前,先体验一下插件使用的具体情形,搭建环境的具体步骤在其他文档中写的很具体,这里不去赘述。
plugins-calendarchart --src --com.fr.plugins.calendarchart --custompie --ChartConfigPane.java --DemoChartsPie.java --demoChartsPieUI.java --PieChart.java --images --pie256.png --web --echarts.bridge.js --echarts.loader.js --echarts.min.js --EChartsFileLoader.java --build.xml --plugin.xml --plugins-calendarchart.iml
此目录下存放第三方图表插件接口调用类,需要实现相应的方法。
此目录下存放插件的相关图片,供报表表单页面展示。
此目录下存放前台js,供图表预览时展示。
该文件用于插件包构建,请参考文档。
该文件包含插件的接入点描述信息,请参考文档。
IEDA项目的配置。
接口详细介绍,请参阅文档。
1) 开发过程:一共三个部分
数据抽取部分:设计器右侧的图表属性表-数据部分
前端展示样式:设计器右侧的图表属性表-样式部分
前端展示的js:图表预览展示的页面
2) 按照自己开发的类修改plugin.xml以及build.xml,请上面提到的文档。
1) 默认数据集和图表属性+ js,除了js,我们需要继承以下三个类,并实现里面具体的方法:
Charts类:图表类
AbstractIndependentChartsProvider类:插件图表接口
AbstractJavaScriptFileHandler类:插件图表界面接口
第一步,继承Charts类,实现以下几个方法:
toJSON():通过getChartData(),这里有你需要的数据,并转成json,前端会拿到这个json
getChartID():返回的应该和plugin.xml配置中的相同
writeXML和readXML可以考虑不自定义实现,如需自定义实现下面会有介绍
public class YourCharts extends Charts { @Override public JSONObject toJSON(Repository repo) throws JSONException { //通过getChartData()得到你需要的数据,把这些数据转成自定义的json格式,前端会收到这个json return null; } @Override public String getChartID() { //这里返回值与plugin.xml配置中的plotID一致。 return yourPlotID; } @Override public void writeXML(XMLPrintWriter xmlPrintWriter) { //图表属性配置信息会被存储在xml中,不考虑自定义图表样式,不用实现。 } @Override public void readXML(XMLableReader xmLableReader) { //和writeXML一样,这里是读配置信息 } }
第二步,继承AbstractIndependentChartsProvider类,实现以下几个方法:
getChartName():返回插件的名字,供报表页面新增图表使用
getChartTypes():返回上一步创建的存储图表数据的类
getRequiredJS():你的图表交互的js
getWrapperName():JS对象名,该对象一般是一个函数,执行后会在给定的dom中绘制图表
getChartImagePath():图表的图标,文件存在images下面
currentAPILevel():当前接口的API等级用于判断是否要升级插件
public class YourChartsProvider extends AbstractIndependentChartsProvider { @Override public String getChartName() { return "你的图表插件名字"; } @Override public Chart[] getChartTypes() { //返回上一步创建的存储图表数据的类,即YourCharts return new YourCharts[]{new YourCharts()}; } @Override public String[] getRequiredJS() { //你的图表交互的js,所依赖的其他js文件,比如echarts.js在另外地方配置,后边会提到 return new String[]{ "/com/fr/plugins/xxx/web/echarts.bridge.js" }; } @Override public String getWrapperName() { //JS对象名,该对象一般是一个函数,执行后会在给定的dom中绘制图表 return "EChartsFactory"; } @Override public String getChartImagePath() { //你的图表插件的图标 return "com/fr/plugins/xxx/images/xxx.png"; } @Override public int currentAPILevel() { //当前接口的API等级用于判断是否要升级插件 return CURRENT_API_LEVEL; } }
第三步,继承AbstractJavaScriptFileHandler类,实现以下几个方法:
pathsForFiles():如果你用的是Echarts的话,返回echarts的js,例如:
return new String[]{ "/com/fr/plugin/parallelchart/web/echarts.loader.js", "/com/fr/plugin/parallelchart/web/echarts.min.js" };
如果你用的是g2的话,返回g2的js,例如:
return new String[]{ "/com/fr/plugin/parallelchart/web/g2.js" };
encode():编码格式
public class EchartFileLoader extends AbstractJavaScriptFileHandler { @Override public String[] pathsForFiles() { //加载js return new String[]{ "/com/fr/plugin/parallelchart/web/echarts.loader.js", "/com/fr/plugin/parallelchart/web/echarts.min.js" }; } @Override public String encode() { return EncodeConstants.ENCODING_UTF_8; }
2) 自定义图表属性+默认数据集 +js
在上边的例子基础上,加上一个自定义属性的配置,以配置图表的标题为例。
第一步,继承ChartsConfigPane的YourChartsConfigPane,通过这个类的populate和update方法与Charts类做交互,实现以下几个方法:
YourChartsConfigPane():构建图表属性面板,以自定义标题为例
Populate():把YourCharts中图表配置的数据在属性面板上展示
Update():把面板上的数据存到YourCharts中去
public class YourChartsConfigPane extends ChartsConfigPane<YourCharts> { private ChartCollection chartCollection; private UITextField value; public YourChartsConfigPane() { //这里构建了一个图表属性面板,创建了一个文本框,用来让用户输入将展示的图表的标题 this.setLayout(new BorderLayout()); northJpane.setLayout(new GridLayout(4, 1, 10,10)); northJpane.add(nameUILabel); northJpane.add(value); northJpane.add(demoUILabel); northJpane.add(customTextArea); this.add(northJpane, BorderLayout.NORTH); this.add(centerJpane, BorderLayout.CENTER); this.setSize(200, 200); this.setVisible(true); //初始化所有组件后必须调用此方法为他们添加监听。注意:此方法必须实现,不实现无法监听更新保存!! initAllListeners(); } //这个类是点击确认按钮时的监听 class ColorEventListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { update(chartCollection); } } @Override //返回的类是类定义中的泛型类 public Class<?extends Charts> accptType() { return YourCharts.class; } @Override public void populate(ChartCollection collection, YourCharts selectedChart) { chartCollection = collection; //把YourCharts中图表配置的数据这边的面板上 value.setText(selectedChart.getCustomData()); } @Override public void update(ChartCollection collection, YourCharts selectedChart) { //把面板上的数据存到YourCharts中去 selectedChart.setCustomData(value.getText()); } }
第二步,继承Charts的YourCharts,默认图表属性时,writeXML和readXML不需要实现,自定义图表属性需要实现这两个方法。
writeXML():把数据存到XML中,具体过程参考demo或者查看文档
readXML():从xml中取数据,具体过程参考demo或者查看文档
public class YourCharts extends Charts { //这个类之前只需要修改writeXML和readXML两个方法 @Override public void writeXML(XMLPrintWriter xmlPrintWriter) { //这里将数据存在XML中 xmlPrintWriter.startTAG(TAG_NAME) .attr("custom", getCustomData()) .end(); writeDefinition(xmlPrintWriter); } @Override public void readXML(XMLableReader xmLableReader) { //这里将XML中的数据读出来 if (xmLableReader.isChildNode()) { String tagName = xmLableReader.getTagName(); if (tagName.equals("customChartDemo")) { setCustomData(xmLableReader.getAttrAsString("custom","111")); } } } }
3) 自定义图表属性+自定义数据集+js
在上边基础上,新加3个类,分别是:定义数据集的面板类,数据集的定义和查询类,查询结果存储类。
第一步,继承AbstractTableDataContentPane,定义数据集的面板类,就是你的插件用户在这个面板上配置他们想要的数据,比如要用哪个表的哪一列数据,里边需要实现的接口作用,如下:
populateBean:同步配置到面板。
updateBean:将面板配置更新
checkBoxUse:检查某些box是否可用
clearAllBoxList:清空设置box的设置
refreshBoxListWithSelectTableData:刷新box
public class TableDataContentPane extends AbstractTableDataContentPane { private UIComboBox dateComboBox; private UIComboBox valueComboBox; public TableDataContentPane(ChartDataPane parent) { dateComboBox = new UIComboBox(); valueComboBox = new UIComboBox(); dateComboBox.setPreferredSize(new Dimension(100, 20)); valueComboBox.setPreferredSize(new Dimension(100, 20)); Component[][] components = new Component[][]{ new Component[]{new UILabel("名称", SwingConstants.RIGHT), dateComboBox}, new Component[]{new UILabel("值", SwingConstants.RIGHT), valueComboBox}}; double p = TableLayout.PREFERRED; double[] columnSize = {p, p}; double[] rowSize = {p, p, p}; JPanel panel = TableLayoutHelper.createTableLayoutPane(components, rowSize, columnSize); setLayout(new BorderLayout()); add(panel, BorderLayout.CENTER); } @Override public void populateBean(ChartCollection collection) { DefaultTableDataDefinition configuration = (DefaultTableDataDefinition) collection.getSelectedChart().getFilterDefinition(); if (configuration == null) return; combineCustomEditValue(dateComboBox, configuration.getDate()); combineCustomEditValue(valueComboBox, configuration.getValue()); } @Override public void updateBean(ChartCollection ob) { DefaultTableDataDefinition myConfiguration = new DefaultTableDataDefinition(); Object wname = dateComboBox.getSelectedItem(); Object wvalue = valueComboBox.getSelectedItem(); if (wname != null) { myConfiguration.setDate(wname.toString()); } if (wvalue != null) { myConfiguration.setValue(wvalue.toString()); } ob.getSelectedChart().setFilterDefinition(myConfiguration); } @Override public void clearAllBoxList() { dateComboBox.removeAll(); valueComboBox.removeAll(); } @Override protected void refreshBoxListWithSelectTableData(List columnNameList) { refreshBoxItems(dateComboBox, columnNameList); refreshBoxItems(valueComboBox, columnNameList) } }
第二步,继承TableDataDefinition,定义数据集的定义和查询类,这个类的功能主要是把用户定义的哪个表哪个列这样的信息,转换成真正的数据(比如这一列的所有值),然后将这些查询结果放在3中提到的查询结果存储类。
public class DefaultTableDataDefinition extends TableDataDefinition { public static final String XML_TAG = "DefaultTableDataDefinition"; private String date; private String value; public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public ChartData createChartData(DataModel resultSet, DataProcessor dataProcessor) { Map<String, String> data = new HashMap<>(); try { int wordNameCol = DataCoreUtils.getColumnIndexByName(resultSet, getDate()); int wordValueCol = DataCoreUtils.getColumnIndexByName(resultSet, getValue()); Map<Object, List<Object>> map = new HashMap<>(); for (int rowIndex = 0; rowIndex < resultSet.getRowCount(); rowIndex++) { Object wordName = resultSet.getValueAt(rowIndex, wordNameCol); Object wordValue = resultSet.getValueAt(rowIndex, wordValueCol); if (wordName != null && wordValue != null) { if (!map.containsKey(wordName)) { map.put(wordName, new ArrayList<>()); } map.get(wordName).add(wordValue); } } for (Map.Entry<Object, List<Object>> entry : map.entrySet()){ data.put(entry.getKey().toString(), entry.getValue().get(0).toString()); } } catch (Exception e) { FRLogger.getLogger().error(e.getMessage(), e); } return new TableDataContent(data); } public void writeXML(XMLPrintWriter writer) { writer.startTAG(XML_TAG) .attr("date", getDate()) .attr("value", getValue()); super.writeXML(writer); writer.end(); } public void readXML(XMLableReader reader) { super.readXML(reader); if (reader.isAttr()) { String tmpVal; if ((tmpVal = reader.getAttrAsString("date", null)) != null){ this.setDate(tmpVal); } if ((tmpVal = reader.getAttrAsString("value", null)) != null){ this.setValue(tmpVal); } } } public boolean equals(Object ob) { return ob instanceof DefaultTableDataDefinition && ComparatorUtils.equals(((DefaultTableDataDefinition) ob).getDate(), this.getDate()) && ComparatorUtils.equals(((DefaultTableDataDefinition) ob).getValue(), this.getValue()) && super.equals(ob); } public Object clone() throws CloneNotSupportedException { DefaultTableDataDefinition cloned = (DefaultTableDataDefinition) super.clone(); cloned.setDate(getDate()); cloned.setValue(getValue()); return cloned; } }
第三步,继承NormalChartData,定义存储2中提到的查询结果的存储类。
public class TableDataContent extends NormalChartData { private final Map<String, String> data; TableDataContent(Map<String, String> data) { this.data = data; } public Map<String, String> getData() { return data; } }
第四步,将自定义的数据集面板,覆盖原先默认的。方法是之前有一个继承了AbstractIndependentChartsUI的类,在里边Override这个方法即可。
@Override public AbstractTableDataContentPane getTableDataSourcePane(Plot plot, ChartDataPane parent) { //这里改成你的类名即可 return new YourDataContentPane(parent); }
4)开发JS
echarts.bridge.js文件中定义预览时调用的函数EChartsFactory,主要分为以下几部分:
部分一,function:定义一些参数取值,不需要变。
EChartsFactory = function(options, $dom) { this.options = options; this.$dom = $dom; this.chartID = options.chartID; this.autoRefreshTime = options.autoRefreshTime || 0; this.width = options.width || $dom.width();// 补充从dom获取. this.height = options.height || $dom.height(); this.sheetIndex = options.sheetIndex || 0; this.ecName = options.ecName || ''; FR.Chart.WebUtils._installChart(this, this.chartID); };
部分二,prototype:页面展示(如果开发者也是用的Echarts的话,只要自定义prototype即可)
EChartsFactory.prototype = { constructor : EChartsFactory, inits : function() { //后台传过来的数据或者样式都在 this.options.chartAttr中 var ct = this.options.chartAttr; //新建一个Echarts图表myChart var myChart = echarts.init(this.$dom[0]); //获取后台传过来的data,并解析 var data = ct.data; var max = 0; for (var i = 0; i < data.length; i += 1) { if (parseInt(max) < parseInt(data[i][1])) { max = data[i][1]; } } var year = echarts.number.parseDate(data[0][0]).getFullYear(); max = max / 20; //获取后台传过来的title var title = ct.title; //设置图表的参数title、tooltip、legend等 option = { } this.newCharts.setOption(ct); }, resize : function() { this.newCharts.resize(); }, refresh:function() { }, refreshData:function(options){ }, //数据监控的刷新方式 setData:function(options, aimation){ } };
为了减少代码中的中文和硬编码,我们可以对其进行国际化。
import com.fr.stable.fun.impl.AbstractLocaleFinder; public class youclassname extends AbstractLocaleFinder { @Override public int currentAPILevel() { return CURRENT_LEVEL; } @Override public String find() { return "com/fr/plugin-XXX/locale/XXX"; } }
在com/fr/plugin-XXX/locale/目录下,添加XXX.properties文件。
Plugin-XXX=XX图
Plugin-XXX_title=标题
….
在plugin.xml中添加youclassname,如下
<extra-core> <LocaleFinder class="com.fr.plugin.xxx.youclassname"/> </extra-core>
1)JAVA中用Inter.getLocText("Plugin-XXX")替换XX图
2)JS中用FR.i18nText("Plugin-XXX ")替换XX图
14 Comments
Anonymous
希望能是一个循序渐进,每步都可以做出一个小插件,比如,完成第一部分,就是一个不用自定义面板的图表插件,完成第二步就是可以自定义属性设置的插件。。。。。。。。。你这样直接是一个大的,万一出错,排查也不好排查的
Anonymous
只能讲看的一头雾水啊。。。这个真的是开发文档么?感觉不连贯啊,不熟悉这个插件开发的估计看懂真不容易吧
Iris Sun
对于初学者,确实一头雾水。。。准备再看一遍 - - |||
Anonymous
中间的是三种方式,而不是三个步骤,应该是使用其中一个就可以
Anonymous
这不是一个循序渐进的文档吗?
1>默认数据集+图表属性+js
2>默认数据集+自定义图表属性+js
3>自定义图表属性+自定义数据集+js'
4>开发js
Anonymous
可能对于熟悉的人来说,你说的这个循序渐进一眼就懂。对于刚接触的人来说,确实一头雾水,完全摸不清。建议像零基础文档部分那样详细展开介绍一下。后面的这几个文档真的是惜字如金的感觉,头疼了。。。。
Anonymous
你跟着这个文档写起来测试项目了没?大神教教我
Iris Sun
看到群里的流程图,结合这篇文章,外加自己DEBUG看数据走向,明白了许多
Anonymous
有谁按照这些步骤成功完成这些不同定义的?
Anonymous
可以的话还是自己跟着代码跑一边,然后再来看这个文档就觉得很简单了
Anonymous
写得真心不好,,完全不知道写啥。。。这教程跟spring那些文档相比,差得有点远。。
Anonymous
写这个文章的人,估计也是随便写的。。
邓伟
写的真心不好,丢人
徐永民
4>开发js 中的事件怎么开放出来,比如单击 双击,使其他的控件能够接受相关的参数。