看板添加loadChart异步加载图表支持

This commit is contained in:
interestinglife 2020-05-31 19:51:47 +08:00
parent 1508c4c1c6
commit 92bd7ae0b0
6 changed files with 286 additions and 56 deletions

View File

@ -11,6 +11,7 @@ import org.datagear.analysis.ChartPlugin;
import org.datagear.analysis.ChartPluginManager;
import org.datagear.analysis.RenderContext;
import org.datagear.analysis.RenderException;
import org.datagear.util.IDUtil;
/**
* 图表部件
@ -81,10 +82,6 @@ public class ChartWidget<T extends RenderContext> extends ChartDefinition
/**
* 生成图表ID
* <p>
* 同一个渲染上下文内允许多次使用同一个{@linkplain ChartWidget}
* 为了保证{@linkplain Chart#getId()}不重复所以这里要生成新的ID
* </p>
*
* @param renderContext
* @return
@ -92,13 +89,6 @@ public class ChartWidget<T extends RenderContext> extends ChartDefinition
*/
protected String generateChartId(T renderContext) throws RenderException
{
String key = "chartIndex-" + getId();
Integer index = renderContext.getAttribute(key);
if (index == null)
index = 0;
renderContext.setAttribute(key, index + 1);
return getId() + "-" + index.toString();
return IDUtil.uuid();
}
}

View File

@ -224,8 +224,12 @@ public class HtmlChartPlugin<T extends HtmlRenderContext> extends AbstractChartP
{
Writer out = renderContext.getWriter();
getHtmlChartScriptObjectWriter().write(out, chart, optionInitialized.getRenderContextVarName(),
optionInitialized.getPluginVarName());
if (optionInitialized.isWriteChartJson())
getHtmlChartScriptObjectWriter().writeJson(out, chart, optionInitialized.getRenderContextVarName(),
optionInitialized.getPluginVarName());
else
getHtmlChartScriptObjectWriter().write(out, chart, optionInitialized.getRenderContextVarName(),
optionInitialized.getPluginVarName());
if (!optionInitialized.isNotWriteInvoke())
{
@ -306,6 +310,7 @@ public class HtmlChartPlugin<T extends HtmlRenderContext> extends AbstractChartP
option.setNotWriteRenderContextObject(false);
option.setNotWriteScriptTag(false);
option.setNotWriteInvoke(false);
option.setWriteChartJson(false);
}
String chartElementId = option.getChartElementId();

View File

@ -48,6 +48,9 @@ public class HtmlChartPluginRenderOption implements Serializable
/** 是否不输出调用渲染函数 */
private boolean notWriteInvoke = false;
/** 写入图表JSON对象而非JS对象 */
private boolean writeChartJson = false;
public HtmlChartPluginRenderOption()
{
super();
@ -217,6 +220,21 @@ public class HtmlChartPluginRenderOption implements Serializable
this.notWriteInvoke = notWriteInvoke;
}
public boolean isWriteChartJson()
{
return writeChartJson;
}
/**
* 设置写入图表JSON对象而非JS对象
*
* @param writeChartJson
*/
public void setWriteChartJson(boolean writeChartJson)
{
this.writeChartJson = writeChartJson;
}
@Override
public String toString()
{
@ -224,7 +242,8 @@ public class HtmlChartPluginRenderOption implements Serializable
+ notWriteChartElement + ", pluginVarName=" + pluginVarName + ", notWritePluginObject="
+ notWritePluginObject + ", chartVarName=" + chartVarName + ", renderContextVarName="
+ renderContextVarName + ", notWriteRenderContextObject=" + notWriteRenderContextObject
+ ", notWriteScriptTag=" + notWriteScriptTag + ", notWriteInvoke=" + notWriteInvoke + "]";
+ ", notWriteScriptTag=" + notWriteScriptTag + ", notWriteInvoke=" + notWriteInvoke
+ ", writeChartJson=" + writeChartJson + "]";
}
/**

View File

@ -67,6 +67,36 @@ public class HtmlChartScriptObjectWriter extends AbstractHtmlScriptObjectWriter
writeNewLine(out);
}
/**
* {@linkplain HtmlChart}的JSON对象写入输出流
* <p>
* 格式为
* </p>
* <code>
* <pre>
* {
* ...,
* renderContext : [renderContextVarName],
* plugin : [pluginVarName],
* ...
* }
* <pre>
* </code>
*
* @param out
* @param chart
* @param renderContextVarName
* @param pluginVarName
* @throws IOException
*/
public void writeJson(Writer out, HtmlChart chart, String renderContextVarName, String pluginVarName)
throws IOException
{
chart = new JsonHtmlChart(chart, renderContextVarName, pluginVarName);
writeJsonObject(out, chart);
}
/**
* 支持JSON输出的{@linkplain HtmlChart}
*

View File

@ -8,6 +8,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
@ -21,11 +22,15 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.datagear.analysis.Chart;
import org.datagear.analysis.ChartTheme;
import org.datagear.analysis.DashboardTheme;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.RenderStyle;
import org.datagear.analysis.TemplateDashboardWidgetResManager;
import org.datagear.analysis.support.ChartWidget;
import org.datagear.analysis.support.ChartWidgetSource;
import org.datagear.analysis.support.html.HtmlChartPluginRenderOption;
import org.datagear.analysis.support.html.HtmlRenderAttributes;
import org.datagear.analysis.support.html.HtmlRenderContext;
import org.datagear.analysis.support.html.HtmlRenderContext.WebContext;
@ -742,54 +747,51 @@ public class DashboardController extends AbstractDataAnalysisController implemen
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setContentType(CONTENT_TYPE_CSS);
Writer out = response.getWriter();
PrintWriter out = response.getWriter();
StringBuilder style = new StringBuilder();
DashboardTheme dashboardTheme = getDashboardThemeAttribute(request.getSession());
ChartTheme chartTheme = (dashboardTheme == null ? null : dashboardTheme.getChartTheme());
if (chartTheme != null)
{
// 表格行
style.append(".dg-chart-table .dg-chart-table-content table.dataTable tbody tr{\n");
style.append("background:" + chartTheme.getBackgroundColor() + ";\n");
style.append("}\n");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable tbody tr{");
out.println(" background:" + chartTheme.getBackgroundColor() + ";");
out.println("}");
// 表格奇数行
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody tr.odd,\n"
+ " .dg-chart-table .dg-chart-table-content table.dataTable.display tbody tr.odd{\n");
style.append("background:" + chartTheme.getBorderColor() + ";\n");
style.append("}\n");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody tr.odd,"
+ " .dg-chart-table .dg-chart-table-content table.dataTable.display tbody tr.odd{");
out.println(" background:" + chartTheme.getBorderColor() + ";");
out.println("}");
// 表格选中悬浮拷贝自/src/main/resources/org/datagear/web/webapp/static/theme/lightness/common.css
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr.hover,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr:hover,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.display tbody tr:hover {\n");
style.append(" background-color: " + chartTheme.getAxisScaleLineColor() + ";\n");
style.append("}\n");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr.hover,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr:hover,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.display tbody tr:hover {");
out.println(" background-color: " + chartTheme.getAxisScaleLineColor() + ";");
out.println("}");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr.hover.selected,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable tbody > tr.selected,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable tbody > tr > .selected,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody > tr.odd.selected,\n");
style.append(
".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody > tr.odd > .selected,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.odd.selected,\n");
style.append(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.odd > .selected,\n");
style.append(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody > tr.selected:hover,\n");
style.append(
".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody > tr > .selected:hover,\n");
style.append(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.selected:hover,\n");
style.append(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr > .selected:hover {\n");
style.append(" background-color: " + chartTheme.getHighlightTheme().getBackgroundColor() + ";\n");
style.append(" color: " + chartTheme.getHighlightTheme().getColor() + ";\n");
style.append("}\n");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody tr.hover.selected,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable tbody > tr.selected,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable tbody > tr > .selected,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody > tr.odd.selected,");
out.println(
".dg-chart-table .dg-chart-table-content table.dataTable.stripe tbody > tr.odd > .selected,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.odd.selected,");
out.println(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.odd > .selected,");
out.println(".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody > tr.selected:hover,");
out.println(
".dg-chart-table .dg-chart-table-content table.dataTable.hover tbody > tr > .selected:hover,");
out.println(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr.selected:hover,");
out.println(
".dg-chart-table .dg-chart-table-content table.dataTable.display tbody > tr > .selected:hover {");
out.println(" background-color: " + chartTheme.getHighlightTheme().getBackgroundColor() + ";");
out.println(" color: " + chartTheme.getHighlightTheme().getColor() + ";");
out.println("}");
}
out.write(style.toString());
}
/**
@ -810,6 +812,74 @@ public class DashboardController extends AbstractDataAnalysisController implemen
return getDashboardData(request, response, model, webContext, paramData);
}
/**
* 动态加载看板图表
*
* @param request
* @param response
* @param model
* @param dashbaordId
* @throws Throwable
*/
@RequestMapping(value = "/loadChart", produces = CONTENT_TYPE_JSON)
public void loadChart(HttpServletRequest request, HttpServletResponse response, org.springframework.ui.Model model,
@RequestParam("dashboardId") String dashboardId, @RequestParam("chartWidgetId") String chartWidgetId,
@RequestParam("chartElementId") String chartElementId)
throws Throwable
{
SessionHtmlTplDashboardManager dashboardManager = getSessionHtmlTplDashboardManagerNotNull(request);
HtmlTplDashboard dashboard = dashboardManager.get(dashboardId);
if (dashboard == null)
throw new RecordNotFoundException();
HtmlTplDashboardWidgetEntity dashboardWidget = (HtmlTplDashboardWidgetEntity) dashboard.getWidget();
// 确保看板创建用户对看板模板内定义的图表有权限
ChartWidgetSourceContext.set(new ChartWidgetSourceContext(dashboardWidget.getCreateUser()));
ChartWidgetSource chartWidgetSource = getHtmlTplDashboardWidgetEntityService()
.getHtmlTplDashboardWidgetRenderer().getChartWidgetSource();
ChartWidget<HtmlRenderContext> chartWidget = chartWidgetSource.getChartWidget(chartWidgetId);
if (chartWidget == null)
throw new RecordNotFoundException();
// 不缓存
response.setContentType(CONTENT_TYPE_JSON);
PrintWriter out = response.getWriter();
RenderStyle renderStyle = resolveRenderStyle(request);
HtmlRenderContext renderContext = createHtmlRenderContext(request, createWebContext(request), renderStyle,
out);
HtmlChartPluginRenderOption renderOption = new HtmlChartPluginRenderOption();
renderOption.setChartElementId(chartElementId);
renderOption.setNotWriteChartElement(true);
renderOption.setPluginVarName(
"{\"id\": \"" + WebUtils.escapeJavaScriptStringValue(chartWidget.getPlugin().getId()) + "\"}");
renderOption.setNotWritePluginObject(true);
renderOption.setChartVarName("undefined");
renderOption.setRenderContextVarName("{}");
renderOption.setNotWriteRenderContextObject(true);
renderOption.setNotWriteScriptTag(true);
renderOption.setNotWriteInvoke(true);
renderOption.setWriteChartJson(true);
HtmlChartPluginRenderOption.setOption(renderContext, renderOption);
Chart chart = chartWidget.render(renderContext);
synchronized (dashboard)
{
List<Chart> charts = dashboard.getCharts();
List<Chart> newCharts = (charts == null || charts.isEmpty() ? new ArrayList<Chart>()
: new ArrayList<Chart>(charts));
newCharts.add(chart);
dashboard.setCharts(newCharts);
}
}
/**
* 解析HTML模板的字符编码
*

View File

@ -89,6 +89,14 @@
$.extend(global.chartFactory.echartsMapURLs, mapUrls);
};
dashboardFactory.loadChartConfig = (dashboardFactory.loadChartConfig ||
{
url: "/analysis/dashboard/loadChart",
dashboardIdParamName: "dashboardId",
chartWidgetIdParamName: "chartWidgetId",
chartElementIdParamName: "chartElementId"
});
//----------------------------------------
// dashboardBase start
//----------------------------------------
@ -165,17 +173,31 @@
data : JSON.stringify(data),
success : function(resultsMap)
{
dashboard.updateCharts(resultsMap);
try
{
dashboard.updateCharts(resultsMap);
}
catch(e)
{
global.chartFactory.logException(e);
}
dashboard.doHandleCharts();
},
error : function()
{
var updateTime = new Date().getTime();
for(var i=0; i<dashboard.charts.length; i++)
dashboard.chartUpdateTime(dashboard.charts[i], updateTime);
},
complete : function()
{
try
{
for(var i=0; i<dashboard.charts.length; i++)
dashboard.chartUpdateTime(dashboard.charts[i], updateTime);
}
catch(e)
{
global.chartFactory.logException(e);
}
dashboard.doHandleCharts();
}
});
@ -361,6 +383,100 @@
return data;
};
/**
* 异步加载图表
*
* @param chartWidgetId 图表部件ID
* @param chartElementId 图表HTML元素ID
* @param ajaxOptions 选填参数参数格式可以是ajax配置项{...}也可以是图表加载成功回调函数function(chart){ ... }
* 如果ajax配置项的success函数图表加载成功回调函数返回false则后续不会自动调用dashboardBase.initLoadedChart函数
*/
dashboardBase.loadChart = function(chartWidgetId, chartElementId, ajaxOptions)
{
var webContext = this.renderContext.webContext;
var loadChartConfig = dashboardFactory.loadChartConfig;
var _this = this;
if(!ajaxOptions)
ajaxOptions = {};
else if(typeof(ajaxOptions) == "function")
{
var successHandler = ajaxOptions;
ajaxOptions =
{
success: successHandler
};
}
var data = {};
data[loadChartConfig.dashboardIdParamName] = _this.id;
data[loadChartConfig.chartWidgetIdParamName] = chartWidgetId;
data[loadChartConfig.chartElementIdParamName] = chartElementId;
var myAjaxOptions = $.extend(
{
url: webContext.contextPath + dashboardFactory.loadChartConfig.url,
data: data,
error: function(jqXHR, textStatus, errorThrown)
{
var msg = (jqXHR.responseJSON ? jqXHR.responseJSON.message : undefined);
if(!msg)
msg = textStatus;
$("#" + chartElementId).html(msg);
}
},
ajaxOptions);
if(!myAjaxOptions.success)
{
myAjaxOptions.success = function(chart, textStatus, jqXHR)
{
_this.inflateLoadedChart(chart);
_this.initLoadedChart(chart);
};
}
else
{
var successHandler = myAjaxOptions.success;
myAjaxOptions.success = function(chart, textStatus, jqXHR)
{
_this.inflateLoadedChart(chart);
var re = successHandler.call(this, chart, textStatus, jqXHR);
if(re != false)
_this.initLoadedChart(chart);
};
}
$.ajax(myAjaxOptions);
};
/**
* 填充异步加载的图表
*
* @param chart 图表JSON对象
*/
dashboardBase.inflateLoadedChart = function(chart)
{
chart.plugin = chartPluginManager.get(chart.plugin.id);
chart.renderContext = this.renderContext;
};
/**
* 初始化异步加载的图表将其加入看板对象准备渲染
*
* @param chart 图表对象
*/
dashboardBase.initLoadedChart = function(chart)
{
global.chartFactory.init(chart);
var charts = (this.charts || []);
this.charts = charts.concat([ chart ]);
};
//----------------------------------------
// dashboardBase end
//----------------------------------------