【仅供内部供应商使用,不提供对外解答和培训】
【仅供内部供应商使用,不提供对外解答和培训】
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 6 Current »
package com.finebi.provider.api.component;
import com.finebi.common.context.OperationContext;
import com.finebi.provider.api.component.data.DataModel;
import com.fr.common.annotations.Open;
import com.fr.stable.fun.mark.Mutable;
import com.fr.web.struct.AssembleComponent;
import java.util.List;
@Open
public interface CustomComponentProvider extends Mutable {
String XML_TAG = "CustomComponentProvider";
int CURRENT_LEVEL = 1;
/**
* 自定义图表名称
*
* @return
*/
String getName();
/**
* 自定义图表类型
*
* @return
*/
String getType();
/**
* 自定义图表 icon
*
* @return
*/
String getIcon();
/**
* 空自定义图表提示,不写默认取 icon
*
* @return
*/
String getPreviewIcon();
/**
* 自定义图表编辑 dom,不写默认取预览
*
* @param var1 上下文
* @return
*/
String getEditPageHTML(OperationContext var1);
/**
* 自定义图表渲染 js、css 注入
*
* @param var1 上下文
* @return
*/
AssembleComponent editClient(OperationContext var1);
/**
* 自定义图表预览 dom,注入依赖文件和挂载节点,可以获取 context
*
* @param var1 上下文
* @return
*/
String getPreviewPageHTML(OperationContext var1);
/**
* 自定义图表渲染 js、css 注入
*
* @param var1 上下文
* @return
*/
AssembleComponent previewClient(OperationContext var1);
/**
* 自定义图表配置文件
*
* @return 配置文件,json字符串
*/
String config();
/**
* 返回是否需要进行自定义数据处理
*
* @param var1 自定义图表相关的上下文信息,目前包含前端传的查询配置
* @return 是否需要进行自定义数据处理
*/
boolean needDataProcess(CustomComponentContext var1);
/**
* 对BI计算后即将返回前端的数据进行自定义处理
*
* @param var1 数据模型
* @param var2 自定义图表相关的上下文信息,目前包含前端传的查询配置
* @return 处理过的数据模型
*/
List<DataModel> process(List<DataModel> var1, CustomComponentContext var2);
}
package com.finebi.provider.api.component;
import com.finebi.common.context.OperationContext;
import com.finebi.provider.api.component.data.DataModel;
import com.fr.stable.fun.mark.API;
import com.fr.web.struct.AssembleComponent;
import java.util.List;
@API(
level = 1
)
public abstract class AbstractCustomComponentProvider implements CustomComponentProvider {
public AbstractCustomComponentProvider() {
}
public String getPreviewIcon() {
return this.getIcon();
}
public String getEditPageHTML(OperationContext context) {
return this.getPreviewPageHTML(context);
}
public AssembleComponent editClient(OperationContext context) {
return this.previewClient(context);
}
public int currentAPILevel() {
return 1;
}
public String mark4Provider() {
return this.getClass().getName();
}
public boolean needDataProcess(CustomComponentContext customComponentContext) {
return false;
}
public List<DataModel> process(List<DataModel> dataModels, CustomComponentContext customComponentContext) {
return dataModels;
}
}
通过上述接口,开发高德地图图表
继承AbstractCustomComponentProvider接口,实现相应功能
import com.finebi.common.context.OperationContext;
import com.finebi.plugin.tptj.ivan.chart.demo.amap.component.MapHotComponent;
import com.finebi.plugin.tptj.ivan.chart.demo.amap.constant.PluginConstantsEK;
import com.finebi.provider.api.component.AbstractCustomComponentProvider;
import com.fr.base.TemplateUtils;
import com.fr.general.IOUtils;
import com.fr.intelli.record.Focus;
import com.fr.intelli.record.Original;
import com.fr.record.analyzer.EnableMetrics;
import com.fr.web.struct.AssembleComponent;
@EnableMetrics
public class MapHotComponentProvider extends AbstractCustomComponentProvider {
/**
* 图表名称
*/
@Override
public String getName() {
return PluginConstantsEK.PLUGIN_MAP_NAME;
}
/**
* @return 图表类型
*/
@Override
public String getType() {
return PluginConstantsEK.PLUGIN_MAP_TYPE;
}
/**
* 图标
*
* @return icon的地址
*/
@Override
public String getIcon() {
try {
String render = TemplateUtils.render("${fineServletURL}");
return render + "/resources?path=/com/finebi/plugin/tptj/ivan/chart/demo/amap/icon.png";
} catch (Exception ignore) {
}
return "";
}
/**
* @param context 上下文
* @return 初始化时的页面元素
*/
@Focus(id = PluginConstantsEK.PLUGIN_ID, text = PluginConstantsEK.PLUGIN_NAME, source = Original.PLUGIN)
@Override
public String getPreviewPageHTML(OperationContext context) {
return "<div id=\"amap-demo-container\"></div>";
}
@Override
public AssembleComponent previewClient(OperationContext context) {
return MapHotComponent.KEY;
}
/**
* 配置项,JSON字符串格式
*
* @return
*/
@Override
public String config() {
return IOUtils.readResourceAsString("com/finebi/plugin/tptj/ivan/chart/demo/amap/config/config.json");
}
}
plugin.xml
<extra-core>
<CustomComponentProvider
class="com.finebi.plugin.tptj.ivan.chart.demo.amap.MapHotComponentProvider"/>
</extra-core>
![]()
实现AbstractCustomComponentProvider接口的config方法,返回值为json格式字符串
{
"dataRegions": [
{
"name": "lat",
"text": "Plugin-DEMO_WEB_LAT"
},
{
"name": "lng",
"text": "Plugin-DEMO_WEB_LNG"
}
],
"attrRegions": [
{
"name": "细粒度",
"text": "Plugin-DEMO_WEB_FG",
"multiFields": false,
"settings": []
}
],
"chartStyles": [
{
"name": "mapProp",
"text": "Plugin-DEMO_WEB_MAP_ATTRIBUTE",
"multiFields": true,
"settings": [
{
"name": "centerLng",
"text": "Plugin-DEMO_WEB_CENTER_LNG",
"type": "Input",
"defaultValue": "102.3716"
},
{
"name": "centerLat",
"text": "Plugin-DEMO_WEB_CENTER_LAT",
"type": "Input",
"defaultValue": "36.6808"
},
{
"name": "defaultZoom",
"text": "Plugin-DEMO_WEB_DEFAULT_ZOOM",
"type": "Input",
"defaultValue": "4"
},
{
"name": "style",
"text": "Plugin-DEMO_WEB_STYLE",
"type": "Select",
"defaultValue": "normal",
"items": [
{
"text": "标准",
"value": "normal"
},
{
"text": "幻影黑",
"value": "dark"
},
{
"text": "月光银",
"value": "light"
},
{
"text": "远山黛",
"value": "whitesmoke"
},
{
"text": "草色青",
"value": "fresh"
},
{
"text": "雅士灰",
"value": "grey"
},
{
"text": "涂鸦",
"value": "graffiti"
},
{
"text": "马卡龙",
"value": "macaron"
},
{
"text": "靛青蓝",
"value": "blue"
},
{
"text": "极夜蓝",
"value": "darkblue"
},
{
"text": "酱籽",
"value": "wine"
}
]
}
]
}
]
}

![]()
注:配置项不支持自定义格式、不支持配置项联动、仅支持以上几种基础格式,按需选择。
配置支持国际化,name代表id,前端js需要通过name获取相应的值,text代表显示名称,可以传入国际化key
{
"dataRegions": [
{
"name": "自定义数据1",
"text": "国际化key"
},
{
"name": "自定义数据2"
}
],
"attrRegions": [
{
"name": "颜色1",
"multiFields": false,
"settings": "color"
},
{
"name": "大小1",
"multiFields": true,
"settings": "size"
},
{
"name": "形状1",
"multiFields": false,
"settings": "symbol"
},
{
"name": "自定选项",
"multiFields": false,
"settings": [
{
"name": "Checkbox",
"type": "Checkbox",
"defaultValue": ["2", "3"],
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
},
{
"text": "选项2",
"value": "3"
}
]
},
{
"name": "RadioGroup",
"type": "RadioGroup",
"defaultValue": "2",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "Segment",
"type": "Segment",
"defaultValue": "2",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "Select",
"type": "Select",
"defaultValue": "1",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "ColorPicker",
"type": "ColorPicker",
"defaultValue": "#ffffff"
},
{
"name": "Input",
"type": "Input",
"defaultValue": "test"
}
]
},
{
"name": "属性1属性1属性1",
"multiFields": true
},
{
"name": "属性2",
"multiFields": false
}
],
"chartStyles": [
{
"name": "自定选项",
"settings": [
{
"name": "Checkbox",
"type": "Checkbox",
"defaultValue": ["2", "3"],
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
},
{
"text": "选项2",
"value": "3"
}
]
},
{
"name": "RadioGroup",
"type": "RadioGroup",
"defaultValue": "2",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "Segment",
"type": "Segment",
"defaultValue": "2",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "Select",
"type": "Select",
"defaultValue": "1",
"items": [
{
"text": "选项1",
"value": "1"
},
{
"text": "选项2",
"value": "2"
}
]
},
{
"name": "ColorPicker",
"type": "ColorPicker",
"defaultValue": "#ffffff"
},
{
"name": "Input",
"type": "Input",
"defaultValue": "test"
}
]
}
]
}
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {}
// 注册渲染方法
new BIPlugin().init(render);
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
// 查找初始化时的dom对象,该对象在 AbstractCustomComponentProvider#getPreviewPageHTML 方法中定义
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
// 获取配置项
const mapAttribute = config["chartStyle"]["地图属性"]["value"];
// 地图组件初始化
const map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);

上述js接口中,data代表其数据维度和指标,config内包含了图形属性和组件样式
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
debugger;
// 查找初始化时的dom对象
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
// 获取配置项
const mapAttribute = config["chartStyle"]["地图属性"]["value"];
// 地图组件初始化
const map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
// 获取全部的经纬度点数据
const points = _getAllPoint(data.dataMapping, data.dataModels[0]);
if (points != null) {
// 创建聚合点图层
var pointCluster = new AMap.MarkerCluster(map, points, {
gridSize: 60, // 设置网格像素大小
renderClusterMarker: _renderCarClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
}
/**
* 自定义聚合点样式
* @param context
* @private
*/
function _renderCarClusterMarker(context) {
let count = context.count;
let factor = context.count / count;
const div = document.createElement('div');
const Hue = 180 - factor * 180;
let bgColor;
if (context.count < 10) {
bgColor = 'hsla(108,100%,40%,1)';
} else if (context.count < 100) {
bgColor = 'hsl(201,100%,40%)';
} else if (context.count < 1000) {
bgColor = 'hsla(36,100%,50%,1)';
} else if (context.count < 10000) {
bgColor = 'hsla(0,100%,60%,1)';
} else {
bgColor = 'hsla(0,100%,40%,1)';
}
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = bgColor;
const shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
/**
* 自定义非聚合点样式
* @param context
* @private
*/
function _renderMarker(context) {
var content = '<div style="background-color: rgba(255,255,178,.9); height: 18px; width: 18px; border: 1px solid rgba(255,255,178,1); border-radius: 12px; box-shadow: rgba(0, 0, 0, 1) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
}
/**
*
* @param dataMapping
* @param dataModel
* @returns {*[]}
* @private
*/
function _getAllPoint(dataMapping, dataModel) {
let lngId = dataMapping['经度'];
let latId = dataMapping['纬度'];
let files = dataModel.fields;
let rowCount = dataModel.rowCount
const colData = dataModel.colData;
let lngIndex;
let latIndex;
// demo这里没有考虑多维度多指标的情况,具体根据实际情况进行修改
files.forEach((item, index) => {
if (lngId.indexOf(item.id) >= 0) {
lngIndex = index;
}
if (latId.indexOf(item.id) >= 0) {
latIndex = index;
}
})
var points = [];
for (let i = 0; i < rowCount; i++) {
points.push({
lnglat: [colData[lngIndex][i], colData[latIndex][i]],
})
}
return points;
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);
注:接口无法直接获取明细数据,若希望使用明细数据,则可将其转为维度,或添加细粒度属性。
通过细粒度进行划分

通过维度进行划分

一般情况下产品本身的处理方式已经够用了,就没必要使用该接口了
场景:产品已有的数据处理方式无法实现客户的需求,希望在数据返回到前端前进行额外的处理
注意:此时插件逻辑中能拿到的数据是产品已经处理过的,可以理解为插件处理在产品处理后。甚至可以替换掉原本的dataModels。具体的处理方式需要自己实现,但要注意性能问题
重写CustomComponentProvider接口中的needDataProcess和process方法
/**
* 是否需要进行数据处理
* true:需要
* false:不需要
* CustomComponentContext: 自定义图表相关配置
*/
@Override
public boolean needDataProcess(CustomComponentContext customComponentContext) {
return true;
}
/**
* 数据处理逻辑
* needDataProcess返回true时才会生效
* CustomComponentContext: 自定义图表相关配置
*/
@Override
public List<DataModel> process(List<DataModel> dataModels, CustomComponentContext customComponentContext) {
return dataModels;
}
期望效果:上述图表DEMO中,每次只随机返回一个点的数据
/**
* 是否需要进行数据处理
* true:需要
* false:不需要
* CustomComponentContext: 自定义图表相关配置
*/
@Override
public boolean needDataProcess(CustomComponentContext customComponentContext) {
return true;
}
/**
* 数据处理逻辑
* needDataProcess返回true时才会生效
* CustomComponentContext: 自定义图表相关配置
*/
@Override
public List<DataModel> process(List<DataModel> dataModels, CustomComponentContext customComponentContext) {
return dataModels.stream().map(dataModel -> new DataModel() {
@Override
public List<Dimension> getFields() {
return dataModel.getFields();
}
@Override
public List<List<Object>> getColData() {
List<List<Object>> colData = new ArrayList<>(dataModel.getFields().size());
dataModel.getColData().forEach(d -> colData.add(Collections.singletonList(d.get((int) (Math.random() * d.size())))));
return colData;
}
}).collect(Collectors.toList());
}

场景:需要客户手动去重新刷新图表,而非整个页面
前端JS调用extensionCallBack('refresh')函数。extensionCallBack即为render方法中的第三个参数
function render(data, config, saveSessionCallback, closeSessionCallBack, extensionCallBack) {
function userClick(){
// 每次执行该方法都会刷新iframe,因此调用前需有逻辑判断。
extensionCallBack('refresh');
}
}
// 注册渲染方法
new BIPlugin().init(render);
期望效果:上述图表DEMO中,每次点击数据点时刷新该图表iframe
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
debugger;
// 查找初始化时的dom对象
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
// 获取配置项
const mapAttribute = config["chartStyle"]["mapProp"]["value"];
// 地图组件初始化
const map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
// 获取全部的经纬度点数据
const points = _getAllPoint(data.dataMapping, data.dataModels[0]);
if (points != null) {
// 创建聚合点图层
var pointCluster = new AMap.MarkerCluster(map, points, {
gridSize: 60, // 设置网格像素大小
renderClusterMarker: _renderCarClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
document.querySelector("#amap-demo-click").onclick = function () {
extensionCallBack('refresh');
}
}
/**
* 自定义聚合点样式
* @param context
* @private
*/
function _renderCarClusterMarker(context) {
let count = context.count;
let factor = context.count / count;
const div = document.createElement('div');
const Hue = 180 - factor * 180;
let bgColor;
if (context.count < 10) {
bgColor = 'hsla(108,100%,40%,1)';
} else if (context.count < 100) {
bgColor = 'hsl(201,100%,40%)';
} else if (context.count < 1000) {
bgColor = 'hsla(36,100%,50%,1)';
} else if (context.count < 10000) {
bgColor = 'hsla(0,100%,60%,1)';
} else {
bgColor = 'hsla(0,100%,40%,1)';
}
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = bgColor;
const shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
/**
* 自定义非聚合点样式
* @param context
* @private
*/
function _renderMarker(context) {
var content = '<div style="background-color: rgba(255,255,178,.9); height: 18px; width: 18px; border: 1px solid rgba(255,255,178,1); border-radius: 12px; box-shadow: rgba(0, 0, 0, 1) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
}
/**
*
* @param dataMapping
* @param dataModel
* @returns {*[]}
* @private
*/
function _getAllPoint(dataMapping, dataModel) {
let lngId = dataMapping['lat'];
let latId = dataMapping['lng'];
let files = dataModel.fields;
let rowCount = dataModel.rowCount
const colData = dataModel.colData;
let lngIndex;
let latIndex;
// demo这里没有考虑多维度多指标的情况,具体根据实际情况进行修改
files.forEach((item, index) => {
if (lngId.indexOf(item.id) >= 0) {
lngIndex = index;
}
if (latId.indexOf(item.id) >= 0) {
latIndex = index;
}
})
var points = [];
for (let i = 0; i < rowCount; i++) {
points.push({
lnglat: [colData[lngIndex][i], colData[latIndex][i]],
})
}
return points;
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);

场景:举个例子,比如地图类需求,用户希望在调用上述刷新接口后,中心点、缩放等级等相关信息,仍和点击刷新前的一致
function render(data, config, saveSessionCallback, closeSessionCallBack, extensionCallBack) {
function userClick(){
// 这里保存的配置{xxx: yyy},将会保存在config.customConfig中。因此只要config.customConfig有数据,读取即可
saveSessionCallback({xxx: yyy});
// 每次执行该方法都会刷新iframe,因此调用前需有逻辑判断。
extensionCallBack('refresh');
}
}
// 注册渲染方法
new BIPlugin().init(render);
期望效果:上述图表DEMO中,每次点击数据点时刷新该图表iframe后,中心点和缩放等级保持不变
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
debugger;
// 查找初始化时的dom对象
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
// 获取配置项
const mapAttribute = config["chartStyle"]["mapProp"]["value"];
// 地图组件初始化
let map;
let customConfig = config.customConfig;
if (customConfig != null && JSON.stringify(customConfig).length > 2) {
// 读取保存的配置
map = new AMap.Map(dom, {
resizeEnable: true,
center: [customConfig.lng, customConfig.lat],
zoom: customConfig.zoom,
});
} else {
map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
}
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
// 获取全部的经纬度点数据
const points = _getAllPoint(data.dataMapping, data.dataModels[0]);
if (points != null) {
// 创建聚合点图层
var pointCluster = new AMap.MarkerCluster(map, points, {
gridSize: 60, // 设置网格像素大小
renderClusterMarker: _renderCarClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
document.querySelector("#amap-demo-click").onclick = function () {
let conf = {
zoom: map.getZoom(),
lng: map.getCenter().lng,
lat: map.getCenter().lat,
};
// 保存配置
saveSessionCallback(conf);
extensionCallBack('refresh');
}
}
/**
* 自定义聚合点样式
* @param context
* @private
*/
function _renderCarClusterMarker(context) {
let count = context.count;
let factor = context.count / count;
const div = document.createElement('div');
const Hue = 180 - factor * 180;
let bgColor;
if (context.count < 10) {
bgColor = 'hsla(108,100%,40%,1)';
} else if (context.count < 100) {
bgColor = 'hsl(201,100%,40%)';
} else if (context.count < 1000) {
bgColor = 'hsla(36,100%,50%,1)';
} else if (context.count < 10000) {
bgColor = 'hsla(0,100%,60%,1)';
} else {
bgColor = 'hsla(0,100%,40%,1)';
}
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = bgColor;
const shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
/**
* 自定义非聚合点样式
* @param context
* @private
*/
function _renderMarker(context) {
var content = '<div style="background-color: rgba(255,255,178,.9); height: 18px; width: 18px; border: 1px solid rgba(255,255,178,1); border-radius: 12px; box-shadow: rgba(0, 0, 0, 1) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
}
/**
*
* @param dataMapping
* @param dataModel
* @returns {*[]}
* @private
*/
function _getAllPoint(dataMapping, dataModel) {
let lngId = dataMapping['lat'];
let latId = dataMapping['lng'];
let files = dataModel.fields;
let rowCount = dataModel.rowCount
const colData = dataModel.colData;
let lngIndex;
let latIndex;
// demo这里没有考虑多维度多指标的情况,具体根据实际情况进行修改
files.forEach((item, index) => {
if (lngId.indexOf(item.id) >= 0) {
lngIndex = index;
}
if (latId.indexOf(item.id) >= 0) {
latIndex = index;
}
})
var points = [];
for (let i = 0; i < rowCount; i++) {
points.push({
lnglat: [colData[lngIndex][i], colData[latIndex][i]],
})
}
return points;
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);

场景:点击时联动仪表板内的其他组件

const currentClicked = {
dId: id,
value: val
};
extensionCallBack(
'click',
currentClicked
);
期望效果:上述图表DEMO中,每次点击非聚合点后,对其他组件数据进行联动
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
debugger;
// 查找初始化时的dom对象
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
const {
// 数据,若横纵轴多指标时图表属性有维度字段,dataModels 为以图表属性为分组的多组数据
dataModels,
// 横纵轴拖入字段的维度 id
dataMapping,
// 图表属性拖入字段的维度 id
chartAttrMapping,
// 图表点击值
clicked
} = data;
const {
// 组件 id
widgetId,
// 全局样式中图表最终样式
globalStyles,
// 图表属性配置所选值
chartAttr,
// 图表样式配置所选值
chartStyle,
// 图标属性中使用形状时的图标映射
symbolIconMap,
// 自定义保存的值(saveSessionCallback)
customConfig
} = config;
// 获取配置项
const mapAttribute = chartStyle["mapProp"]["value"];
// 地图组件初始化
let map;
if (customConfig != null && JSON.stringify(customConfig).length > 2) {
// 读取保存的配置
map = new AMap.Map(dom, {
resizeEnable: true,
center: [customConfig.lng, customConfig.lat],
zoom: customConfig.zoom,
});
} else {
map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
}
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
// 获取全部的经纬度点数据
const points = _getAllPoint(dataMapping, dataModels[0]);
if (points != null) {
// 创建聚合点图层
var pointCluster = new AMap.MarkerCluster(map, points, {
gridSize: 60, // 设置网格像素大小
renderClusterMarker: _renderCarClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
document.querySelector("#amap-demo-click").onclick = function () {
let conf = {
zoom: map.getZoom(),
lng: map.getCenter().lng,
lat: map.getCenter().lat,
};
// 保存配置
saveSessionCallback(conf);
extensionCallBack('refresh');
}
/**
* 自定义聚合点样式
* @param context
* @private
*/
function _renderCarClusterMarker(context) {
let count = context.count;
let factor = context.count / count;
const div = document.createElement('div');
const Hue = 180 - factor * 180;
let bgColor;
if (context.count < 10) {
bgColor = 'hsla(108,100%,40%,1)';
} else if (context.count < 100) {
bgColor = 'hsl(201,100%,40%)';
} else if (context.count < 1000) {
bgColor = 'hsla(36,100%,50%,1)';
} else if (context.count < 10000) {
bgColor = 'hsla(0,100%,60%,1)';
} else {
bgColor = 'hsla(0,100%,40%,1)';
}
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = bgColor;
const shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
/**
* 自定义非聚合点样式
* @param context
* @private
*/
function _renderMarker(context) {
var content = '<div style="background-color: rgba(255,255,178,.9); height: 18px; width: 18px; border: 1px solid rgba(255,255,178,1); border-radius: 12px; box-shadow: rgba(0, 0, 0, 1) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
let datum = context.data[0];
let id = datum.idId;
const val = [
{
dId: datum.idId,
text: datum.idVal,
},
// {
// dId: datum.lngId,
// text: datum.lngVal,
// },
// {
// dId: datum.latId,
// text: datum.latVal,
// }
];
/**
* dId: 当前维度的Id
* value: 当前维度和之前维度的id及对应值 (不是全部的维度信息!)
*/
const currentClicked = {
dId: id,
value: val
};
context.marker.on('click', ev => {
debugger;
// 触发联动
extensionCallBack(
'click',
currentClicked
);
})
}
}
/**
*
* @param dataMapping
* @param dataModel
* @returns {*[]}
* @private
*/
function _getAllPoint(dataMapping, dataModel) {
let lngId = dataMapping['lng'];
let latId = dataMapping['lat'];
let files = dataModel.fields;
let rowCount = dataModel.rowCount
const colData = dataModel.colData;
let lngIndex;
let latIndex;
let idIndex;
// demo这里没有考虑多维度多指标的情况,具体根据实际情况进行修改
files.forEach((item, index) => {
if (lngId.indexOf(item.id) >= 0) {
lngIndex = index;
} else if (latId.indexOf(item.id) >= 0) {
latIndex = index;
} else {
idIndex = index;
}
})
var points = [];
for (let i = 0; i < rowCount; i++) {
points.push({
lnglat: [colData[lngIndex][i], colData[latIndex][i]],
lngId: files[lngIndex].id,
latId: files[latIndex].id,
idId: files[idIndex].id,
lngVal: colData[lngIndex][i],
latVal: colData[latIndex][i],
idVal: colData[idIndex][i],
})
}
return points;
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);

场景:点击时联动其他组件,或进行跳转
请根据用户的数据类型,选择合适的事件
const currentClicked = {
id1: value1,
id2: value2,
id3: value3
};
// 点击指标事件
extensionCallBack("pointSelected", {
pos: {
x: 鼠标位置x,
y: 鼠标位置y,
},
// 点击指标的维度id
measure: measureId,
// 点击指标所在行的各个字段值,包括指标和维度
// id: 字段值
row: currentClicked,
});
// 点击维度事件
extensionCallBack("dimensionSelected", {
pos: {
x: 鼠标位置x,
y: 鼠标位置y,
},
// 点击指标的维度id
measure: measureId,
// 点击指标所在行的各个字段值,包括指标和维度
// id: 字段值
row: currentClicked,
});
期望效果:上述图表DEMO中,每次点击非聚合数据后,可以联动或跳转
(function ($) {
function render(
data, // 数据
config, // 配置
saveSessionCallback,
closeSessionCallBack,
extensionCallBack
) {
debugger;
// 查找初始化时的dom对象
const dom = document.getElementById("amap-demo-container");
// 一定要设置 宽高 不然图表渲染不出来
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
const {
// 数据,若横纵轴多指标时图表属性有维度字段,dataModels 为以图表属性为分组的多组数据
dataModels,
// 横纵轴拖入字段的维度 id
dataMapping,
// 图表属性拖入字段的维度 id
chartAttrMapping,
// 图表点击值
clicked
} = data;
const {
// 组件 id
widgetId,
// 全局样式中图表最终样式
globalStyles,
// 图表属性配置所选值
chartAttr,
// 图表样式配置所选值
chartStyle,
// 图标属性中使用形状时的图标映射
symbolIconMap,
// 自定义保存的值(saveSessionCallback)
customConfig
} = config;
// 获取配置项
const mapAttribute = chartStyle["mapProp"]["value"];
// 地图组件初始化
let map;
if (customConfig != null && JSON.stringify(customConfig).length > 2) {
// 读取保存的配置
map = new AMap.Map(dom, {
resizeEnable: true,
center: [customConfig.lng, customConfig.lat],
zoom: customConfig.zoom,
});
} else {
map = new AMap.Map(dom, {
resizeEnable: true,
center: [mapAttribute[0], mapAttribute[1]],
zoom: mapAttribute[2],
});
}
// 地图设置样式
const styleName = "amap://styles/" + mapAttribute[3];
map.setMapStyle(styleName);
// 获取全部的经纬度点数据
const points = _getAllPoint(dataMapping, dataModels[0]);
if (points != null) {
// 创建聚合点图层
var pointCluster = new AMap.MarkerCluster(map, points, {
gridSize: 60, // 设置网格像素大小
renderClusterMarker: _renderCarClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
window.addEventListener("resize", function () {
dom.style.width = document.body.clientWidth + "px";
dom.style.height = document.body.clientHeight + "px";
});
document.querySelector("#amap-demo-click").onclick = function () {
let conf = {
zoom: map.getZoom(),
lng: map.getCenter().lng,
lat: map.getCenter().lat,
};
// 保存配置
saveSessionCallback(conf);
extensionCallBack('refresh');
}
/**
* 自定义聚合点样式
* @param context
* @private
*/
function _renderCarClusterMarker(context) {
let count = context.count;
let factor = context.count / count;
const div = document.createElement('div');
const Hue = 180 - factor * 180;
let bgColor;
if (context.count < 10) {
bgColor = 'hsla(108,100%,40%,1)';
} else if (context.count < 100) {
bgColor = 'hsl(201,100%,40%)';
} else if (context.count < 1000) {
bgColor = 'hsla(36,100%,50%,1)';
} else if (context.count < 10000) {
bgColor = 'hsla(0,100%,60%,1)';
} else {
bgColor = 'hsla(0,100%,40%,1)';
}
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = bgColor;
const shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
/**
* 自定义非聚合点样式
* @param context
* @private
*/
function _renderMarker(context) {
var content = '<div style="background-color: rgba(255,255,178,.9); height: 18px; width: 18px; border: 1px solid rgba(255,255,178,1); border-radius: 12px; box-shadow: rgba(0, 0, 0, 1) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
let datum = context.data[0];
// let id = datum.idId;
// const val = [
// {
// dId: datum.idId,
// text: datum.idVal,
// },
// {
// dId: datum.lngId,
// text: datum.lngVal,
// },
// {
// dId: datum.latId,
// text: datum.latVal,
// }
// ];
// /**
// * dId: 当前维度的Id
// * value: 当前维度和之前维度的id及对应值 (不是全部的维度信息!)
// */
// const currentClicked = {
// dId: id,
// value: val
// };
// context.marker.on('click', ev => {
// debugger;
// // 触发联动
// extensionCallBack(
// 'click',
// currentClicked
// );
// });
const currentClicked = {
[datum.idId]: datum.idVal,
[datum.lngId]: datum.lngVal,
[datum.latId]: datum.latVal
};
const currentId = datum.lngId
context.marker.on('click', ev => {
debugger;
// 触发联动
const demo = {
pos: {
x: window.event.pageX,
y: window.event.pageY,
},
// 点击指标的维度id
measure: currentId,
// 点击指标所在行的各个字段值,包括指标和维度
// id: 字段值
row: currentClicked,
};
// 点击指标事件(数据栏里是指标【绿色的】使用该方法)
// extensionCallBack("pointSelected", demo);
// 点击维度事件(数据栏里是维度【蓝色的】使用该方法)
// extensionCallBack("dimensionSelected", demo);
for (let i = 0; i < data.dataModels[0].fields.length; i++) {
let field = data.dataModels[0].fields[i];
if (field.id === currentId) {
// 这里判断用户的数据栏里是维度还是指标
if (field.isDimension) {
extensionCallBack("dimensionSelected", demo);
} else if (field.isMeasure) {
extensionCallBack("pointSelected", demo);
}
break;
}
}
});
}
}
/**
*
* @param dataMapping
* @param dataModel
* @returns {*[]}
* @private
*/
function _getAllPoint(dataMapping, dataModel) {
let lngId = dataMapping['lng'];
let latId = dataMapping['lat'];
let files = dataModel.fields;
let rowCount = dataModel.rowCount
const colData = dataModel.colData;
let lngIndex;
let latIndex;
let idIndex;
// demo这里没有考虑多维度多指标的情况,具体根据实际情况进行修改
files.forEach((item, index) => {
if (lngId.indexOf(item.id) >= 0) {
lngIndex = index;
} else if (latId.indexOf(item.id) >= 0) {
latIndex = index;
} else {
idIndex = index;
}
})
var points = [];
for (let i = 0; i < rowCount; i++) {
points.push({
lnglat: [colData[lngIndex][i], colData[latIndex][i]],
lngId: files[lngIndex].id,
latId: files[latIndex].id,
idId: files[idIndex].id,
lngVal: colData[lngIndex][i],
latVal: colData[latIndex][i],
idVal: colData[idIndex][i],
})
}
return points;
}
// 注册渲染方法
new BIPlugin().init(render);
})(jQuery);

非基本控件即视为自定义组件,提供 value 和 setValue 处理数据
// 自定义组件渲染
<Widget
type={type}
value={currentValue}
setValue={(v: any) => {
this.store.updateConfig(v, boxName, index);
}}
/>;
// 插件 demo
// json 配置
{
"name": "自定义组件",
"type": "bi.test.widget",
"defaultValue": "1"
}
// 自定义组件实现,插件中实现,需要单独注入到 subjectPage
// bi.test.widget
render() {
const { value, setValue } = this.options;
return (
<TextValueCombo
simple
value={value}
height={24}
items={[
{
text: '选项1',
value: '1',
},
{
text: '选项2',
value: '2',
},
]}
listeners={[
{
eventName: TextValueCombo.EVENT_CHANGE,
action: v => {
setValue(v);
},
},
]}
/>
);
}
场景:需要实现一些复杂的配置项
请根据用户的数据类型,选择合适的事件
这里以编辑框为例
!(function () {
/**
* 自定义组件需优先注入到subject里
*/
var Demo = BI.inherit(BI.Widget, {
props: {
baseCls: ""
},
render: function () {
var self = this;
// self.options包含上次的value和setValue方法
debugger;
return {
type: "bi.vertical",
width: 300,
items: [
{
el: {
type: "bi.vertical_adapt",
items: [
{
el: {
type: "bi.label",
text: "名称",
textAlign: "left",
},
width: 70,
rgap: 10,
},
{
el: {
type: "bi.editor",
value: self.options.value, // 上次设置的value
cls: "bi-border bi-border-radius",
width: 150,
allowClear: !1,
height: 24,
ref: function (e) {
self.myNameEditor = e;
},
listeners: [
{
eventName: "EVENT_CHANGE",
action: function () {
debugger;
// 设置value的方法,调用后会刷新页面
self.options.setValue(this.getValue());
},
},
],
},
},
],
},
tgap: 10,
}]
};
},
});
BI.shortcut("bi.amap.demo", Demo);
})();
配置json文件中,指定对应的配置
![]()

【仅供内部供应商使用,不提供对外解答和培训】