Skip to content

DataFocus插件图形开发流程

1 . 插件图形文件的构成部分

插件图形文件内部是由一个_DataFocusPluginChart对象构成的

var _DataFocusPluginChart = {
// 插件图形名
// 插件图形的出图条件
// 插件图形的Axis配置
.....
}

- [必填项] 插件图形名/ 显示名 / 描述

    chartType: 'df-plugin-echarts-column' ,         //  图形名 [ * 必填项 ]
    chartDisplayNameKey: "chart-name-title" ,       //  图形名称中英文Key [ * 必填项 ]
    chartDescriptionKey: "chart-description-title" ,//  图形描述中英文Key [ * 必填项 ]

- [必填项][Object] needAxis 插件图形Axis的配置

目前图形Axis只接受固定两组:
(1)xAxis ; yAxis ; legend ; size ; time ;
(2)Columns ; rows ; values ;

// 图形配置元素 [ * 必填项 ]
    needAxis: {
        // 默认xAxis是属性列 [数组]
        'xAxis': {
            i18nKey: "chart-x-axis-title" ,         //  X轴显示名的key [ 非必填项 ]
            allowMeasureXAxis: true ,               //  是否允许数值类型的X轴 [ 非必填项 ]
        } ,
        // 默认yAxis是数值列 [数组]
        'yAxis': {
            i18nKey: "chart-y-axis-title" ,             //  Y轴显示名的key [ 非必填项 ]
            allowMultiYAxis: true ,                     //  是否允许左右侧边Y轴 [ 非必填项 ]
            multiYAxisKey: "chart-multi-y-axis-title" , //  右侧轴的显示名的key [ 非必填项 ]

            /**
             * 默认Y轴的数量 [ 非必填项 ]
             * 允许系统默认图形配置元素后 ,
             * needAxis中存在Y轴,默认Y轴的数量,—1时即所有的除Size以外的数值列 ;
             * 如果为>0的数值,则取相应数量以内的数值列
             */
            defaultYAxisNumber: -1
        } ,
        // 默认图例时distinct_count在50以内的属性列 [ 数值 ]
        'legend': {
            i18nKey: "chart-legend-title"               //  Legend显示名的Key  [ 非必填项 ]
        } ,
    } ,

- [必填项][function] isSatisfiedTheChart 插件图形的出图判断方法

    /**
     * 判断图形允许画的方法 [ * 必填项 ]
     * 适合柱状图的条件 , 聚合数据且至少一个属性列及至少一个数值列
     * @param searchResultStatistic
     *          {
     *              aggregatedFlag  搜索数据是否聚合的标志
     *              measureColumns    数值列数量
     *                  {
     *                      index
                            columnId
                            columnClassify
                            parent                            
                            statistics
     *                  }
     *              attributeColumns  属性列数量
     *              legendColumns     能作为图例的属性列数量(distinct_count < 50 的列)
     *              dateColumns       日期列数量
     *              lonColumns        经度列数量
     *              latColumns        纬度列数量
     *              queryResultCount    查询数据结果条目
     *              regionColumns        地理列数组(1:国家集合;1.5:国家;2:省集合;2.5:省;3:市集合;3.5:市;4:区集合;4.5:区;0:其他地理列)
     *          }
     *
     * @return Boolean  true: 当前数据适合画当前图形;false: 当前数据不适合画当前图形
     * */
    isSatisfiedTheChart: function(searchResultStatistic){ return true;  } ,

- [function] getSatisfiedChartAxis 设置needAxis中各个轴的可选项的方法

方法的参数于isSatisfiedTheChart 方法的参数结构一致

/**
 * [选填] 获取当前图形各个needAxis中可选择的配置列
 * @return
 *      {
 *          // 依据needAxis结构返回各个可选列的index
 *          xAxis: [1,2,3] ,
 *          yAxis: [4,5] ,
 *          legend: [1,2,3] ,
 *          size: [4,5] ,
 *          time: [1,2]
 *      }
 * */
getSatisfiedChartAxis: function(searchResultStatistic){
    let self = this ;       //  是当前_DataFocusChartTypes中的对象

    const needAxis = self.needAxis || {};

    if(!needAxis || !searchResultStatistic) return false;
    let theAxisObj = {} ;
    // Time / legend / xAxis / yAxis / size
    // X轴的可选项
    if(needAxis.xAxis){
        theAxisObj.xAxis = searchResultStatistic.attributeColumns&& searchResultStatistic.attributeColumns.map(function (h) { return h.idx; }) ;
    }
    // Y轴的可选项
    if(needAxis.yAxis){
        theAxisObj.yAxis = searchResultStatistic.measureColumns && searchResultStatistic.measureColumns.map(function (h) { return h.idx; }) ;
    }
    // 大小列的可选项
    if(needAxis.size){
        theAxisObj.size = searchResultStatistic.measureColumns && searchResultStatistic.measureColumns.map(function (h) { return h.idx; }) ;
    }
    // 图例列的可选项
    // 有XAxis时,图例则必须在有X轴的情况下才可以有,所以在只有一个属性列时,不允许存在图例列
    if(needAxis.legend){
        if(needAxis.xAxis && searchResultStatistic.attributeColumns && searchResultStatistic.attributeColumns.length <= 1){
            theAxisObj.legend = [] ;
        }else{
            theAxisObj.legend = searchResultStatistic.legendColumns && searchResultStatistic.legendColumns.map(function (h) { return h.idx; }) ;
        }
    }
    // 时间列的可选项
    if(needAxis.time){
        if(needAxis.xAxis && searchResultStatistic.attributeColumns && searchResultStatistic.attributeColumns.length <= 1){
            theAxisObj.time = [] ;
        }else{
            theAxisObj.time = searchResultStatistic.dateColumns && searchResultStatistic.dateColumns.map(function (h) { return h.idx; }) ;
        }
    }

    if(_DataFocusMethod.isObjEmpty(theAxisObj)){
        return false ;
    }else{
        theAxisObj.name = self.name ;
        theAxisObj.type = self.name ;
        return theAxisObj ;
    }
} ,

- [function] initChartAxisConfig初始化当前图轴配置的方法

默认情况下,图形的图轴由系统按一定规则默认分配,如果当前图形不想使用系统的默认图轴分配规则,可自定义默认的图轴配置

/**
 * [选填] 在图形没有图轴配置的情况下,从getSatisfiedChartAxis中图轴可选项中初始化画图配置
 *
 * 初始化规则:
 * 1. 如果当前数据中有两个以上数值列,则默认X轴是数值列,Y轴是一个数值列
 * 2. 如果当前数据只有一个数值列,则默认X轴是属性列,Y轴是数值列
 *
 * @param options
 *      {
 *          tableData   -   查询数据
 *      }
 * // 依据needAxis生成初始的图轴配置值,及各个轴的配置可选项
 *    返回对象名字规则: 可选项是needAxis中各项名字 + Area ; 当前配置项是 cur + needAxis中各项名字
 * @return chartAxis
 *          {
 *              xAxisArea: [  {col_id , index ...} , {} ] ,
 *              curXAxis: [ {col_id , index ...} ]
 *              yAxisArea: [ {col_id , index , min , max ...} , {} ] ,
 *              curYAxis: [ {col_id , index , min , max ...} ] ,
 *              legendArea: [ {col_id , index ...} ],
 *              curLegend: {col_id , index ...} ,
 *              sizeArea: [{col_id , index ...}] ,
 *              curSize: {col_id , index ...} ,
 *              timeArea: [ {col_id , index ...} ] ,
 *              curTime: {col_id , index ...}
 *          }
 *
 * */
initChartAxisConfig: function(options){
    options = options || {} ;
    if(!options.tableData || !options.tableData.headers) return false ;

    let self = this ,               //  是当前_DataFocusChartTypes中的对象
        tableData = options.tableData ,
        headers = tableData.headers  ,
        columns = tableData.columns ;

    console.log("设置scatter初始的图轴配置");
    // charts是依据各个图形中的getSatisfiedChartAxis生成的
    let theMatchedChart = tableData.charts && tableData.charts.find(function(chart){ return (chart.name || chart.type) === self.name ; }) ,
        chartAxis = {} ;
    if(!theMatchedChart){ console.log("当前数据不适合散点图"); return chartAxis ; }

    let getIdIndexInHeader = function(colIdx , headers){
        // 目前charts返回的都是索引
        if(colIdx < headers.length) return colIdx ;

        for(let i = 0 ; i < headers.length ; i++){
            if(headers[i].col_id === colIdx)
                return i ;
        }
        return -1 ;
    } ;
    // 获取图例列及可选数组 [ 默认legend不允许与时间列相同 ]
    if(self.needAxis && self.needAxis.legend){
        chartAxis.legendArea = [];
        let theLegendArea = [] ;
        theMatchedChart.legend && theMatchedChart.legend.forEach(function (c) {
            let curColumnIndex = getIdIndexInHeader(c, headers);
            let obj = headers[curColumnIndex];
            obj.index = curColumnIndex;
            chartAxis.legendArea.push(obj);
            theLegendArea.push(obj) ;
        });

        // 默认使用值distinct count最小的作为图例
        theLegendArea.sort(function(a , b){return a.statistics && b.statistics && a.statistics['unique count'] - b.statistics['unique count'] ; }) ;
        chartAxis.curLegend = theLegendArea[0];
    }

    // 获取XAxis列及可选数组
    if(self.needAxis && self.needAxis.xAxis){
        chartAxis.xAxisArea = [];
        theMatchedChart.xAxis && theMatchedChart.xAxis.forEach(function (c) {
            let curColumnIndex = getIdIndexInHeader(c, headers);
            let obj = $.extend({}, headers[curColumnIndex]);
            obj.header = headers[curColumnIndex];
            obj.index = curColumnIndex;
            chartAxis.xAxisArea.push(obj);
        });

        // 赋值默认的X轴,默认X轴与图例列不能重叠
        let xAxisArea = chartAxis.xAxisArea.filter(function (xAxis) {
            return !(chartAxis.curLegend && xAxis.index === chartAxis.curLegend.index);
        });
        // 如果指令了属性列的数量
        let defaultXAxisNumber = _DataFocusMethod.isPureNumber(self.needAxis.xAxis.defaultXAxisNumber)?self.needAxis.xAxis.defaultXAxisNumber:-1;
        let withoutSortX = xAxisArea && xAxisArea.filter(function(head){ return !head.isSortNoFlag ; }) ;
        // 如果没有排序列的属性列足够分配需要的X轴,则优先使用非排序列的属性列
        if(defaultXAxisNumber < 0){
            chartAxis.curXAxis = withoutSortX.length ? withoutSortX : xAxisArea ;
        }else{
            if(withoutSortX.length >= defaultXAxisNumber){
                chartAxis.curXAxis = withoutSortX.slice(0 , defaultXAxisNumber) ;
            }else{
                chartAxis.curXAxis = xAxisArea.slice(0 , defaultXAxisNumber) ;
            }
        }
    }

    // 获取YAxis列及可选数组 [ 必须计算yAxis的最大值与最小值,计算刻度使用 ]
    if(self.needAxis && self.needAxis.yAxis){
        chartAxis.yAxisArea = [];
        theMatchedChart.yAxis && theMatchedChart.yAxis.forEach(function (c) {
            let curColumnIndex = getIdIndexInHeader(c, headers);
            let obj = $.extend({}, headers[curColumnIndex]);
            obj.header = headers[curColumnIndex];
            obj.index = curColumnIndex;
            obj.direction = "left";
            let min = obj.statistics && obj.statistics.min ,
                max = obj.statistics && obj.statistics.max ;
            // 如果min/max中存在Infinity这类的无穷数据 或者 存在非法数字时,需要从原始数据中重新获取合法数值
            if(!isFinite(min + max) || !_DataFocusMethod.isPureNumber(min) || !_DataFocusMethod.isPureNumber(max)) {
                let theMin = Infinity, theMax = -Infinity;
                columns && columns.forEach(function (d) {
                    if (_DataFocusMethod.isPureNumber(d[curColumnIndex]) && isFinite(d[curColumnIndex])) {
                        theMin = Math.min(theMin, d[curColumnIndex]);
                        theMax = Math.max(theMax, d[curColumnIndex]);
                    }
                });
                min = theMin ;
                max = theMax ;
                // 如果theMin是Infinity, 说明数据表中没有合法的数值,则将其设置成0
                if(Math.abs(theMin) === Infinity) { min = 0 ; max = 0 ;}
            }
            // 如果min是undefined 说明,数据表头中没有统计 且 返回数据中没有合法的数值,则默认设置为0
            if(min === undefined){min = 0 ; max = 0 ;}
            obj.min = min ;
            obj.max = (min === max && max === 0 && min === 0) ? 1 : max;

            chartAxis.yAxisArea.push(obj);
        }) ;

        // 如果有图例列,则Y轴只允许1个;将Y轴中的curSize的列清除
        if(chartAxis.curLegend){
            let theYAxis = chartAxis.yAxisArea.find(function(yAxis){ return true ; }) ;
            if(theYAxis) chartAxis.curYAxis = [theYAxis];
        }else{
            // 没有图例的情况,使用配置中默认的Y轴数量
            let yAxisArea = chartAxis.yAxisArea ;

            let defaultYAxisNumber = _DataFocusMethod.isPureNumber(self.needAxis.yAxis.defaultYAxisNumber)?self.needAxis.yAxis.defaultYAxisNumber:1 ;
            // 如果默认Y轴< 0 , 说明剩下所有可用Y轴全部是默认Y轴
            if(defaultYAxisNumber < 0){
                chartAxis.curYAxis = yAxisArea ;
            }else{
                chartAxis.curYAxis = yAxisArea.slice(0 , defaultYAxisNumber) ;
            }
        }
    }

    // 如果X轴允许配置数值列-------------------------------------------------------------
    // 如果允许X轴是数值列 , 则将数值列加入X轴的可选区域中[ yAxis多时,X轴默认为measure列 ]
    if(self.needAxis && self.needAxis.xAxis && self.needAxis.xAxis.allowMeasureXAxis){
        chartAxis.xAxisArea = [].concat(chartAxis.xAxisArea , chartAxis.yAxisArea);

        // 有Size时,将size使用的数值列分配出去后,如果还有2个以上的数值列,且允许X轴时数值列,则默认x为数值列
        let tempYArea = chartAxis.yAxisArea && chartAxis.yAxisArea.filter(function(yAxis){
            return (chartAxis.curSize && yAxis.idx !== chartAxis.curSize.idx) || !chartAxis.curSize ;
        });
        if(tempYArea && tempYArea.length >= 2){
            chartAxis.curXAxis = tempYArea.slice(0 , 1);
            chartAxis.curYAxis = tempYArea.slice(1 , 2);
        }
    }

    // 因为Legend与Time将属性列全部使用完了,这种时候默认不需要legend , 将Legend分配给XAxis
    if(self.needAxis && self.needAxis.xAxis){
        if(chartAxis.curLegend && !chartAxis.curXAxis.length){
            chartAxis.curXAxis = chartAxis.xAxisArea.filter(function (xAxis) {
                return xAxis.index === chartAxis.curLegend.index ;
            });
            chartAxis.curLegend = undefined ;
        }
    }

    return chartAxis ;
} ,

- [必填项][function] validateAxisConfig 检验插件图形XY轴配置是否合法的方法

/**
     * 判断当前图形的Axis配置是否合法 [ * 必填项 ]
     * 例:X轴不允许为空,Y轴不允许为空,X轴不允许与图例列相同
     * @params [Object]theAxisConfig      -   当前用户手动配置的规则
     *          {
     *              xAxis: [{
     *                  index
     *                  columnId
     *                  type
     *              }] ,
     *              yAxis: [{
     *                  index
     *                  columnId
     *              }] ,
     *              legend: [{
     *                  index
     *                  columnId
     *              }] ,
     *
     *              // 搜素数据的统计信息
     *              searchResultStatistic: {
     *                  aggregatedFlag  搜索数据是否聚合的标志
     *
     *                  measureColumns    数值列数量
     *                  attributeColumns  属性列数量
     *                  legendColumns     能作为图例的属性列数量(distinct_count < 50 的列)
     *                  dateColumns       日期列数量
     *                  lonColumns        经度列数量
     *                  latColumns        纬度列数量
     *                  queryResultCount    查询数据结果条目
     *                  regionColumns        地理列数组(1:国家集合;1.5:国家;2:省集合;2.5:省;3:市集合;3.5:市;4:区集合;4.5:区;0:其他地理列)
     *              }
     *          }
     *
     * 判断条件:
     * 1. xAxis不允许为空
     * 2. yAxis不允许为空
     * 3. 主Y轴不允许为空
     * 4. legend只允许一个
     * 5. legend存在时Y轴只允许一个
     * 6. legend不允许与xAxis重复
     *
     * @return [String]errorKey -   错误提示对应的key;
     * */
    validateAxisConfig: function(theAxisConfig) { let errorKey = ‘’; return errorKey ;}

- [必填项][Array] chartConfigureList图表配置

组件类型type: select / color / input / theme

/ 当前图形的配置项[ *新增图形必填项 ]
    chartConfigureList: [
        {
            key: "category_general",
            title: "category_chart_general",
            options: [
                {
                    //  配置名,唯一标识[ *必填项 ]
                    key: "chart_test_select",
                    //  配置名的中英文key,在对应的i18nObj中有对应的key:value值[ * 必填项 ]
                    title: "option_chart_test_select",
                    //  配置详细描述的中英文key,在对应的i18nObj中有对应的key:value值[ * 必填项 ]
                    description: "option_chart_test_select_desc",
                    //  配置类型  select - 下拉框类型  ; text - 文本输入框 ; input - 数值输入框 ; checkbox  - 复选框 ; color - 颜色选择器
                    type: "select",
                    // select配置时的下拉选项列表 , 下拉选项需要在对应的i18nObj中有对应的key:value值 [ * select 配置中的必填项 ]
                    selection: [
                        "option_test_default",
                        "option_test_default1",
                        "option_test_default2",
                        "option_test_default3"
                    ]
                } 
           ]
}
]

- [Object] chartConfigureAdvanced 图表配置的拓展

/**
 * 图形配置的辅助对象:当前图形定义的宏列表 及 宏 解析方法 ; 适配之前图形配置值的方法 ;
 *
 * 当前图形提供的宏列表[ 宏名的命名规则: 开头是%_ 用_连接的大写字符字符串 ]
 * 供自定义数据标签 及 悬浮文本使用,可以让用户配置显示内容
 * 键值对  MacroName : MacroDisplayNameKey
 * */
chartConfigureAdvanced: {
    // 根据宏配置列表,已足够外部组件将 %_NAME:%_VALUE 翻译成 [属性]:[值] 显示给用户
    // 如果当前图形支持换行的宏,则输入时允许输入换行符 ; 否则编辑时也不支持换行符 ;
    chartMacroList: [
        {
            key: '%_NAME' ,
            nameKey: '%_NAME' ,
            descriptionKey: '%_NAME_desc' ,
        } ,
        {
            key: '%_CATEGORY_NAME' ,
            nameKey: '%_CATEGORY_NAME' ,
            descriptionKey: '%_CATEGORY_NAME_desc' ,
        } ,
        {
            key: '%_BR' ,
            nameKey: '%_BR' ,
            descriptionKey: '%_BR_desc' ,
        }
    ] ,
    // 依据当前配置,将 %_NAME:%_VALUE 转译成真实值显示
    /**
     * @param configMacroStr    -   用户配置的带宏的内容
     * @param options
     *          {
     *              type        -
     *
     *              xLabel       -   图形单元对应的属性值 用于实现%_NAME %_CATEGORY_NAME
     *              xLabelIndex  -   图形单元对应的属性索引,用于实现 %_CATEGORY_NUMBER
     *              xLabelTotal  -   图形单元对应的属性上所有值的总和用于实现 %_CATEGORY_TOTAL  %_CATEGORY_AVERAGE  %_PERCENT_OF_CATEGORY
     *              xLabelNumber -   图形单元对应的属性上所有值的个数用于实现 %_CATEGORY_TOTAL  %_CATEGORY_AVERAGE  %_PERCENT_OF_CATEGORY
     *              yValue       -   图形单元对应的值 用于实现 %_VALUE
     *              yAxis        -   图形单元对应的值列 用于实现 %_VALUE_NAME  %_SERIES_NAME
     *              legend       -   图形单元对应的图例名(为空时说明是多Y轴) 用于实现 %_SERIES_NAME
     *              legendIndex  -   图形单元对应的图例索引 用于实现 %_SERIES_NUMBER
     *              legendTotal  -   图形单元对应的图例上所有值的总和,用于实现 %_PERCENT_OF_TOTAL
     *              rowData -  图形单元对应的一整行数据 用于实现%_COLUMN_N的宏
     *          }
     * */
    translateMacro: function(configMacroStr , options){
        options = options || {} ;
        if(!configMacroStr) return false ;

        let macroRegex = /%(_[A-Z0-9]*){1,}/gi ;
        let theMatchedTextValue = '' ,  matchedIndex = 0;
        macroRegex.lastIndex = -1 ;
        for(let i = 0 , execResult ; (execResult = macroRegex.exec(configMacroStr)) && i < 100 ; i++){
            if(!execResult || !execResult[0][0]) break ;
            let theMatchStr = execResult[0] ,
                theMatchIndex = execResult.index ;

            theMatchedTextValue += configMacroStr.substring(matchedIndex , Math.max(theMatchIndex , matchedIndex)) ;
            switch (theMatchStr) {
                case "%_NAME":
                case "%_CATEGORY_NAME":
                    theMatchedTextValue += options.xLabel || '' ;
                    break ;
                case "%_CATEGORY_NUMBER":
                    theMatchedTextValue += options.xLabelIndex || '' ;
                    break ;
                case "%_BR":
                    theMatchedTextValue += options.type === 'label' ? '\n' : '<br/>' ;
                    break;
                default:
                    if(theMatchStr.startsWith("%_COLUMN_")){
                        let theColumnN = parseInt(theMatchStr.substring(9)) ;
                        if(!isNaN(theColumnN) && options.rowData && theColumnN > 0 && theColumnN <= options.rowData.length){
                            theMatchedTextValue += options.rowData[theColumnN -1] ;
                        }
                    }
                    break;
            }

            matchedIndex = theMatchIndex + theMatchStr.length ;
        }

        if(matchedIndex < configMacroStr.length){
            theMatchedTextValue += configMacroStr.substring(matchedIndex) ;
        }

        return theMatchedTextValue ;
    } ,

    // 如果当前图形配置在上线后需要更新配置,需要兼容行为,则在此处处理[ 不同图形兼容方法不一样 ]
    compatibleHistoryConfigure: function(chartConfigure , options){
        if(!options) return false ;
        options = options || {} ;
        // 最大高度限制 变了名字
        if(chartConfigure.attribute_label_max_height !== undefined){
            chartConfigure.x_axis_label_max_height_percent = chartConfigure.attribute_label_max_height * 100 ;
            chartConfigure.attribute_label_max_height = undefined ;
        }        
    }
}

- [必填项] [function]drawChart画图方法

插件图形注入进_DataFocus之后,查询完数据,如果数据满足当前图形的画图条件,则会再准备好画图元素后,进入当前画图方法中,会将当前图形对象的画图上下文传入插件图形的画图方法drawChart中

    /**
     * 使用个人画图方法画图形 [*必填项]
     * @params [Object]options  -   当前图形的配置
     *         {
     *               container       -   当前画图的DOM元素
     *               chartAxis       -   当前图形的Axis配置
     *               datas           -   当前画图数据
     *               isResize        -   是否是窗口变化引起的画图[ 如果只是resize可以不需要重新处理数据 ]
     *               chartConfigures -   图形配置
     *               mousedownCallback   -   图形中元素鼠标事件的回调
     *               plugins         -   DataFocus提供的第三方画图库
     *                  {
     *                        d3  		-   d3的v3版本
     *                        echarts 	-   echarts的
*					d3v7		-	d3的v7版本
*					mathTool	-	数学计算方法集合
     *                  }
     *           }
     * **/
    drawChart: function(options){
        options = options || {} ;
        if(!options.container){console.error('DataFocus没有提供画图元素Dom'); return false ; }
        if(!options.currentChart){ console.error('DataFocus没有提供图形实例'); return false ; }
        if(!options.chartAxis){ console.error('DataFocus没有图形的配置信息'); return false ; }
        if(!options.datas){ console.error('DataFocus没有提供画图数据'); return false ; }
        options.container.innerHTML = '新的插件Echarts柱状图形' ;
    } ,

插件图形中元素左右键响应机制

插件图形的画图方法drawChart会接收参数mousedownCallback方法,供插件图形中的元素被鼠标点击时的响应;

需要往mousedownCallback方法中传入的参数列表:

    if(typeof options.mousedownCallback === 'function'){
        options.mousedownCallback(d3.event , {
            TheEventButton: 0 ,			  // 用户伪装鼠标事件左右键【*非必填项】
            dataColumnIndex: [0 , 1] ,      //  图形元素使用的数据属性列的Index数组【*必填项】
            dataRowIndex: 22 ,              //  图形元素使用的数据行数 【*必填项】
        })
     }

因为插件图形在看板中可以适配左键按钮联动的行为,适配右键按钮查看数据明细等操作,所以插件图形中元素被点击时需要将该元素使用的数据行数(dataRowIndex)及对应的属性列数组(dataColumnIndex)传入mousedownCallback中,_DataFocusChart会响应改事件触发联动或右键菜单;

额外的参数TheEventButton是为了适配非鼠标事件需要被伪装成鼠标事件的情况,theEventButton=0时,伪装的是左键;非0时,伪装的是右键;

- [必填项] [Object] i18nObj 插件图形的国际化对象

// 当前图形定制的中英文[ 配置XY轴,图表配置 ] [ *新增图形必填项 ]
i18nObj: {
    chinese: {
        "chart-name-title": "Echarts柱状图" ,
        "chart-description-title": "至少一个属性列,一个数值列" ,

        "chart-x-axis-title": "X轴" ,
        "chart-y-axis-title": "Y轴" ,
        "chart-multi-y-axis-title": "右侧副Y轴" ,
        "chart-legend-title": "图例" ,
    } ,
    english: {
        "chart-name-title": "Echarts Column" ,
        "chart-description-title": "At least one attribute column an one measure column " ,

        "chart-x-axis-title": "X Axis" ,
        "chart-y-axis-title": "Y Axis" ,
        "chart-multi-y-axis-title": "The second Y Axis" ,
        "chart-legend-title": "Legend",
    }
}

- [必填项][function] getTemplateData 样例数据的生成方法

// 为模板问答生成样例数据 [*必填项]
    getTemplateData: function(){
        let theTemplateData = {
            headers: [{
                idx: 0,
                col_id: '10001',
                col_uuid: '10001',
                col_name: '测试属性列',
                data_type: 'ATTRIBUTE',
                operator: '',
                geo_type: '',
                col_type: 'string'
            },{
                idx: 1 ,
                col_id: '10002' ,
                col_uuid: '10002',
                col_name: '测试数值列' ,
                data_type: 'MEASURE',
                operator: 'SUM' ,
                geo_type: '' ,
                col_type: 'int',
                statistics: {
                    sum: 123 ,
                    average: 32 ,
                    min: 5 ,
                    max: 40 ,
                    'standard deviation': 2.5 ,
                    'unique count': 10 ,
                    'variance': 23.5
                }
            }] ,
            columns: [
                ['测试1' , 11] ,
                ['测试2' , 22] ,
                ['测试3' , 33] ,
                ['测试4' , 44] ,
            ],
            default_chart: {
                type: 'df-plugin-echarts-column' ,
                xAxis: [0] ,
                yAxis: [1]
            } ,
            charts: [{
                type: 'df-plugin-echarts-column' ,
                xAxis: [0] ,
                yAxis: [1]
            }]
        };

        return theTemplateData ;
    } ,

2.插件图形注入DataFocus方法

- 测试样例

具体内容请参考 df-plugin-echarts-column.js样例插件图形文件:

/** 柱状图画图组件 **/
var _DataFocusPluginChart = {
	
    chartType: 'df-plugin-echarts-test' ,         //  图形名 [ * 必填项 ]
    chartDisplayNameKey: "chart-name-title" ,       //  图形名称中英文Key [ * 必填项 ]
    chartDescriptionKey: "chart-description-title" ,//  图形描述中英文Key [ * 必填项 ]


    /**
     * 判断图形允许画的方法 [ * 必填项 ]
     * 适合柱状图的条件 , 聚合数据且至少一个属性列及至少一个数值列
     * @param searchResultStatistic
     *          {
     *              aggregatedFlag  搜索数据是否聚合的标志
     *
     *              measureColumns    数值列数量
     *              attributeColumns  属性列数量
     *              legendColumns     能作为图例的属性列数量(distinct_count < 50 的列)
     *              dateColumns       日期列数量
     *              lonColumns        经度列数量
     *              latColumns        纬度列数量
     *              queryResultCount    查询数据结果条目
     *              regionColumns        地理列数组(1:国家集合;1.5:国家;2:省集合;2.5:省;3:市集合;3.5:市;4:区集合;4.5:区;0:其他地理列)
     *          }
     *
     * @return Boolean  true: 当前数据适合画当前图形;false: 当前数据不适合画当前图形
     * */
    isSatisfiedTheChart: function(searchResultStatistic){
        let satisfiedFlag = false ;

        // 搜索数据是聚合数据且至少一个属性列及至少一个数值列
        if(searchResultStatistic && searchResultStatistic.aggregatedFlag && searchResultStatistic.attributeColumns && searchResultStatistic.measureColumns ){
            if(searchResultStatistic.measureColumns.length > 0 && searchResultStatistic.attributeColumns.length > 0 ){
                satisfiedFlag = true ;
            }
        }

        return satisfiedFlag ;
    } ,

    // 图形配置元素 [ * 必填项 ]
    needAxis: {
        // 默认xAxis是属性列 [数组]
        'xAxis': {
            i18nKey: "chart-x-axis-title" ,         //  X轴显示名的key [ 非必填项 ]
            allowMeasureXAxis: true ,               //  是否允许数值类型的X轴 [ 非必填项 ]
        } ,
        // 默认yAxis是数值列 [数组]
        'yAxis': {
            i18nKey: "chart-y-axis-title" ,             //  Y轴显示名的key [ 非必填项 ]
            allowMultiYAxis: true ,                     //  是否允许左右侧边Y轴 [ 非必填项 ]
            multiYAxisKey: "chart-multi-y-axis-title" , //  右侧轴的显示名的key [ 非必填项 ]

            /**
             * 默认Y轴的数量 [ 非必填项 ]
             * 允许系统默认图形配置元素后 ,
             * needAxis中存在Y轴,默认Y轴的数量,—1时即所有的除Size以外的数值列 ;
             * 如果为>0的数值,则取相应数量以内的数值列
             */
            defaultYAxisNumber: -1
        } ,
        // 默认图例时distinct_count在50以内的属性列 [ 数值 ]
        'legend': {
            i18nKey: "chart-legend-title"               //  Legend显示名的Key  [ 非必填项 ]
        } ,
    } ,

    /**
     * 判断当前图形的Axis配置是否合法 [ * 必填项 ]
     * 例:X轴不允许为空,Y轴不允许为空,X轴不允许与图例列相同
     * @params [Object]theAxisConfig      -   当前用户手动配置的规则
     *          {
     *              xAxis: [{
     *                  index
     *                  columnId
     *                  type
     *              }] ,
     *              yAxis: [{
     *                  index
     *                  columnId
     *              }] ,
     *              legend: [{
     *                  index
     *                  columnId
     *              }] ,
     *
     *              // 搜素数据的统计信息
     *              searchResultStatistic: {
     *                  aggregatedFlag  搜索数据是否聚合的标志
     *
     *                  measureColumns    数值列数量
     *                  attributeColumns  属性列数量
     *                  legendColumns     能作为图例的属性列数量(distinct_count < 50 的列)
     *                  dateColumns       日期列数量
     *                  lonColumns        经度列数量
     *                  latColumns        纬度列数量
     *                  queryResultCount    查询数据结果条目
     *                  regionColumns        地理列数组(1:国家集合;1.5:国家;2:省集合;2.5:省;3:市集合;3.5:市;4:区集合;4.5:区;0:其他地理列)
     *              }
     *          }
     *
     * 判断条件:
     * 1. xAxis不允许为空
     * 2. yAxis不允许为空
     * 3. 主Y轴不允许为空
     * 4. legend只允许一个
     * 5. legend存在时Y轴只允许一个
     * 6. legend不允许与xAxis重复
     *
     * @return [String]errorKey -   错误提示对应的key;
     * */
    validateAxisConfig: function(theAxisConfig) {
        let theErrorMsgKey = '' ;

        if(!theAxisConfig) {
            theErrorMsgKey = 'chart-axis-config-null-error' ;
            return theErrorMsgKey ;
        }

        // 1.X轴不允许为空
        if(!theAxisConfig.xAxis || !theAxisConfig.xAxis.length){
            theErrorMsgKey = 'chart-xy-axis-null' ;
            return theErrorMsgKey ;
        }
        // 2.Y轴不允许为空
        if(!theAxisConfig.yAxis || !theAxisConfig.yAxis.length){
            theErrorMsgKey = 'chart-xy-axis-null' ;
            return theErrorMsgKey ;
        }
        // 3.主Y轴不允许为空
        let theMainYAxis = theAxisConfig.yAxis.filter(function(yAxis){ return yAxis.direction !== 'right' ; }) ;
        if(!theMainYAxis || !theMainYAxis.length){
            theErrorMsgKey = 'chart-left-y-axis-null' ;
            return theErrorMsgKey ;
        }
        // 图例不为空的情况下
        if(theAxisConfig.legend){
            // 3. 图例只允许一个
            if(theAxisConfig.legend.length > 1){
                theErrorMsgKey = 'chart-legend-too-much' ;
                return theErrorMsgKey ;
            }
            // 4. 有图例的情况下,Y轴不允许多个
            if(theAxisConfig.legend.length && theAxisConfig.yAxis.length> 1){
                theErrorMsgKey = 'chart-y-axis-too-much-with-legend' ;
                return theErrorMsgKey ;
            }
            // 5. 有图例的情况下,图例不允许与X轴相同
            for(let i = 0 ; i < theAxisConfig.legend.length ; i ++){
                let theLegend = theAxisConfig.legend[i] ,
                    theMatchedLegend = theAxisConfig.xAxis && theAxisConfig.xAxis.find(function(xAxis){
                        return xAxis.index === theLegend.index ;
                    });

                if(theMatchedLegend){
                    theErrorMsgKey = 'chart-x-legend-axis-not-equal' ;
                    return theErrorMsgKey ;
                    break ;
                }
            }
        }
        // 当前图形需要将所有属性列利用起来
        let theSearchResultStatistic = theAxisConfig.searchResultStatistic ;
        if(theSearchResultStatistic){
            let theUsedAttribute = 0 ;
            if(theAxisConfig.xAxis){ theUsedAttribute += theAxisConfig.xAxis.length ;}
            if(theAxisConfig.legend){ theUsedAttribute += theAxisConfig.legend.length ;}

            if(theSearchResultStatistic.attributeColumns && theSearchResultStatistic.attributeColumns.length > theUsedAttribute){
                theErrorMsgKey = 'chart-need-all-attribute-columns' ;
                return theErrorMsgKey ;
            }
        }

        return theErrorMsgKey ;
    } ,

    // 当前图形的配置项[ *新增图形必填项 ]
    chartConfigureList: [
        {
            key: "category_general",
            title: "category_chart_general",
            options: [
                {
                    //  配置名,唯一标识[ *必填项 ]
                    key: "chart_test_select",
                    //  配置名的中英文key,在对应的i18nObj中有对应的key:value值[ * 必填项 ]
                    title: "option_chart_test_select",
                    //  配置详细描述的中英文key,在对应的i18nObj中有对应的key:value值[ * 必填项 ]
                    description: "option_chart_test_select_desc",
                    //  配置类型  select - 下拉框类型  ; text - 文本输入框 ; input - 数值输入框 ; checkbox  - 复选框 ; color - 颜色选择器
                    type: "select",
                    // select配置时的下拉选项列表 , 下拉选项需要在对应的i18nObj中有对应的key:value值 [ * select 配置中的必填项 ]
                    selection: [
                        "bar",
                        "line",
                        "scatter",
                        "pie"
                    ]
                } ,

                {
                    key: "chart_test_input_text",
                    title: "option_chart_test_input_text",
                    description: "option_chart_test_input_text_desc",
                    type: "text"
                } ,

                {
                    key: "chart_test_input_number",
                    title: "option_chart_test_input_number",
                    description: "option_chart_test_input_number_desc",
                    type: "input",
                    defaultNumber: 10 ,     //  当前数值输入框的默认值 [ 非必填项 ]
                    min: 0 ,        //  当前数值输入框的最小值 [ 非必填项 ]
                    max: 100        //  当前数值输入框的最大值 [ 非必填项 ]
                } ,

                {
                    key: "chart_test_checkbox",
                    title: "option_chart_test_checkbox",
                    description: "option_chart_test_checkbox_desc",
                    type: "checkbox"
                } ,

                {
                    key: "chart_test_color",
                    title: "option_chart_test_color",
                    description: "option_chart_test_color_desc",
                    type: "color"
                } ,

                {
                    key: "chart_test_measure_color_array",
                    title: "option_chart_test_measure_color_array",
                    description: "option_chart_test_measure_color_array",
                    type: "colorArray"
                } ,

                {
                    key: "chart_test_attribute_color_array",
                    title: "option_chart_test_attribute_color_array",
                    description: "option_chart_test_attribute_color_array",
                    type: "attrColorArray"
                } ,
            ]
        }
    ] ,

    /**
     * 使用个人画图方法画图形 [*必填项]
     * @params [Object]options  -   当前图形的配置
     *              {
     *                  container       -   当前画图的DOM元素
     *                  chartAxis       -   当前图形的Axis配置
     *                  datas           -   当前画图数据
     *                  isResize        -   是否是窗口变化引起的画图
     *                  chartConfigures -   图形配置
     *                  plugins         -   DataFocus提供的第三方画图库
     *                      {
     *                          d3  -   d3的v3版本
     *                          echarts -   echarts的
     *                      }
     *              }
     * **/
    drawChart: function(options){
        options = options || {} ;

        if(!options.container){console.error('DataFocus没有提供画图元素Dom'); return false ; }
        if(!options.currentChart){ console.error('DataFocus没有提供图形实例'); return false ; }
        if(!options.chartAxis){ console.error('DataFocus没有图形的配置信息'); return false ; }
        if(!options.datas){ console.error('DataFocus没有提供画图数据'); return false ; }
        
        console.log(options.chartConfigures)
        console.log(options.datas)

        options.container.removeAttribute("_echarts_instance_")
        var myChart = options.plugins.echarts.init(options.container);
        var option;
        option = {
            xAxis: {
                type: 'category',
                data: []
            },
            yAxis: {
                type: 'value'
            },
            series: [{
                data: [],
                type: 'bar',
                showBackground: true,
                backgroundStyle: {
                    color: 'rgba(180, 180, 180, 0.2)'
                }
            }]
        };
        
        if(options.chartConfigures){
            if(options.chartConfigures.hasOwnProperty('chart_test_color')){
                option.series[0].backgroundStyle.color=options.chartConfigures.chart_test_color
            }
            if(options.chartConfigures.hasOwnProperty('chart_test_checkbox')){
                option.series[0].showBackground=options.chartConfigures.chart_test_checkbox
            }
            if(options.chartConfigures.hasOwnProperty('chart_test_select')){
                option.series[0].type=options.chartConfigures.chart_test_select
            }
        }
        options.datas.columns.forEach(item => {
            option.xAxis.data.push(item[1])
            option.series[0].data.push(item[0])
        });
        option && myChart.setOption(option);
        console.log("画图了")
    } ,

    // 为模板问答生成样例数据
    getTemplateData: function(){
        let theTemplateData = {
            headers: [{
                idx: 0,
                col_id: '10001',
                col_uuid: '10001',
                col_name: '测试属性列',
                data_type: 'ATTRIBUTE',
                operator: '',
                geo_type: '',
                col_type: 'string'
            },{
                idx: 1 ,
                col_id: '10002' ,
                col_uuid: '10002',
                col_name: '测试数值列' ,
                data_type: 'MEASURE',
                operator: 'SUM' ,
                geo_type: '' ,
                col_type: 'int',
                statistics: {
                    sum: 123 ,
                    average: 32 ,
                    min: 5 ,
                    max: 40 ,
                    'standard deviation': 2.5 ,
                    'unique count': 10 ,
                    'variance': 23.5
                }
            }] ,
            columns: [
                ['测试1' , 11] ,
                ['测试2' , 22] ,
                ['测试3' , 33] ,
                ['测试4' , 44] ,
            ],
            default_chart: {
                type: 'df-plugin-echarts-column' ,
                xAxis: [0] ,
                yAxis: [1]
            } ,
            charts: [{
                type: 'df-plugin-echarts-column' ,
                xAxis: [0] ,
                yAxis: [1]
            }]
        };

        return theTemplateData ;
    } ,

    // 当前图形定制的中英文[ 配置XY轴,图表配置 ] [ *新增图形必填项 ]
    i18nObj: {
        chinese: {
            "chart-name-title": "图形插件测试" ,
            "chart-description-title": "至少一个属性列,一个数值列" ,

            "chart-x-axis-title": "X轴" ,
            "chart-y-axis-title": "Y轴" ,
            "chart-multi-y-axis-title": "右侧副Y轴" ,
            "chart-legend-title": "图例" ,

            "chart-axis-config-null-error": "系统没有传入当前用户的Axis配置信息" ,
            "chart-xy-axis-null": "X轴或Y轴不能为空" ,
            "chart-left-y-axis-null": "左侧主Y轴不允许为空" ,
            "chart-legend-too-much": "图例只允许一个" ,
            "chart-y-axis-too-much-with-legend": "图例存在时,Y轴只允许一个" ,
            "chart-x-legend-axis-not-equal": "X轴不允许与图例相同" ,
            "chart-need-all-attribute-columns": "当前图形需要将所有的属性列利用起来",

            "category_chart_general": "通用" ,
            "option_chart_color_theme": "颜色主题",
            "option_chart_test_select": "测试下拉框" ,
            "option_chart_test_select_desc": "测试下拉框描述详情" ,
            "option_test_default": "测试下拉选项",
            "option_test_default1": "测试下拉选项1",
            "option_test_default2": "测试下拉选项2",
            "option_test_default3": "测试下拉选项3",

            "option_chart_test_input_text":"测试文本输入框",
            "option_chart_test_input_text_desc":"测试文本输入框描述详情",


            "option_chart_test_input_number":"测试数字输入框",
            "option_chart_test_input_number_desc":"测试数字输入框描述详情",


            "option_chart_test_checkbox":"测试复选框",
            "option_chart_test_checkbox_desc":"测试复选框描述详情",


            "option_chart_test_color":"测试颜色选择器",
            "option_chart_test_color_desc":"测试颜色选择器描述详情",

            "option_chart_test_measure_color_array": "测试数值颜色区间",
            "option_chart_test_measure_color_array_desc": "测试数值颜色区间描述详情",

            "option_chart_test_attribute_color_array": "测试属性颜色",
            "option_chart_test_attribute_color_array_desc": "测试属性颜色描述详情",
        } ,
        english: {
            "chart-name-title": "Chart Plugin Test" ,
            "chart-description-title": "At lease an attribute column and a measure column" ,

            "chart-x-axis-title": "X Axis" ,
            "chart-y-axis-title": "Y Axis" ,
            "chart-multi-y-axis-title": "The second Y Axis" ,
            "chart-legend-title": "Legend",

            "chart-axis-config-null-error": "系统没有传入当前用户的Axis配置信息" ,
            "chart-xy-axis-null": "X轴或Y轴不能为空" ,
            "chart-left-y-axis-null": "左侧主Y轴不允许为空" ,
            "chart-legend-too-much": "图例只允许一个" ,
            "chart-y-axis-too-much-with-legend": "图例存在时,Y轴只允许一个" ,
            "chart-x-legend-axis-not-equal": "X轴不允许与图例相同" ,
            "chart-need-all-attribute-columns": "当前图形需要将所有的属性列利用起来",

            "category_chart_general": "通用" ,
            "option_chart_color_theme": "颜色主题",
            "option_chart_test_select": "测试插件图形中的配置名" ,
            "option_chart_test_select_desc": "测试插件图形中的配置描述详情" ,
            "option_test_default": "测试默认选项",
            "option_test_default1": "测试默认选项1",
            "option_test_default2": "测试默认选项2",
            "option_test_default3": "测试默认选项3",
        }
    } ,
}

- 本地调试时注入方法

本地构造样例数据,构造drawChart方法的参数,本地测试画图方法是否正常

    import pluginChart from '/fdemo/static/js/df-plugin-echarts-column.js' ;
    import d3 from 'd3' ;
    import echarts from 'echarts' ;
    pluginChart.drawChart({
        container: $('#chart-container') ,
        chartAxis: {
            curXAxis: [{
                col_uuid: '' ,
                
            }] ,
            curYAxis: [] ,
            curLegend: {
            }
        } ,
        datas: { 本地构造的样例数据 },
        chartConfigures: {
            chart_test_select: 'option_test_default1'
        } ,
        plugins: {
            d3: d3 ,
            echarts: echarts
        }
    }) ;

- 将插件图形加入加入DataFocus系统时注入方法

后期DataFocus会开放上传插件文件的入口,将本地开发的图形文件注入到系统中,手动启动系统的检测机制,检测成功则可以进入系统使用;

在系统管理-》全局配置-》插件图形中增加插件图形

新增插件图形,此处的插件图形名称必须与文件内的chartType保持一致

增加完图形插件后,系统会检测当前插件图形成功后,在搜索页面既可使用

3.插件图形限制

不允许内部引入其他地址中的插件,只允许使用DataFocus中提供的插件

  • d3[ v3版本 ]
  • Echarts [ 4.X版本 ]