改进看板渲染策略,所有图表插件独立渲染至chartPluginManager.js内,看版内仅引用

This commit is contained in:
interestinglife 2020-05-29 23:46:25 +08:00
parent 4e8dcf546b
commit 1591146872
9 changed files with 337 additions and 86 deletions

View File

@ -68,6 +68,9 @@ public class HtmlChartPlugin<T extends HtmlRenderContext> extends AbstractChartP
/** 图表HTML元素标签名 */
private String elementTagName = "div";
/** 上次修改时间 */
private long lastModified = -1;
/** 图表脚本换行符 */
private String newLine = HTML_NEW_LINE;
@ -102,6 +105,21 @@ public class HtmlChartPlugin<T extends HtmlRenderContext> extends AbstractChartP
this.elementTagName = elementTagName;
}
/**
* 获取上次修改时间
*
* @return -1 表示未修改
*/
public long getLastModified()
{
return lastModified;
}
public void setLastModified(long lastModified)
{
this.lastModified = lastModified;
}
public String getNewLine()
{
return newLine;

View File

@ -345,55 +345,6 @@ public class HtmlChartPluginLoader
return null;
}
/**
* 从指定目录加载单个{@linkplain HtmlChartPlugin}返回{@code null}表示文件不合法
*
* @param directory
* @return
* @throws HtmlChartPluginLoadException
*/
protected HtmlChartPlugin<?> loadSingleForDirectory(File directory) throws HtmlChartPluginLoadException
{
File chartFile = new File(directory, FILE_NAME_PLUGIN);
if (!chartFile.exists())
return null;
HtmlChartPlugin<?> plugin = null;
Reader chartIn = null;
try
{
chartIn = IOUtil.getReader(chartFile, this.encoding);
JsDefContent jsDefContent = this.htmlChartPluginJsDefResolver.resolve(chartIn);
if (!StringUtil.isEmpty(jsDefContent.getPluginJson())
&& !StringUtil.isEmpty(jsDefContent.getPluginChartRenderer()))
{
plugin = createHtmlChartPlugin();
this.jsonChartPluginPropertiesResolver.resolveChartPluginProperties(plugin, jsDefContent.getPluginJson());
plugin.setChartRenderer(new StringJsChartRenderer(jsDefContent.getPluginChartRenderer()));
plugin.setIcons(toBytesIconsInDirectory(directory, plugin.getIcons()));
if (StringUtil.isEmpty(plugin.getId()) || StringUtil.isEmpty(plugin.getNameLabel()))
plugin = null;
}
}
catch (Exception e)
{
throw new HtmlChartPluginLoadException(e);
}
finally
{
IOUtil.close(chartIn);
}
return plugin;
}
protected HtmlChartPlugin<?> loadSingleForZip(File zip) throws HtmlChartPluginLoadException
{
ZipInputStream in = null;
@ -439,6 +390,59 @@ public class HtmlChartPluginLoader
}
}
/**
* 从指定目录加载单个{@linkplain HtmlChartPlugin}返回{@code null}表示文件不合法
*
* @param directory
* @return
* @throws HtmlChartPluginLoadException
*/
protected HtmlChartPlugin<?> loadSingleForDirectory(File directory) throws HtmlChartPluginLoadException
{
File chartFile = new File(directory, FILE_NAME_PLUGIN);
if (!chartFile.exists())
return null;
HtmlChartPlugin<?> plugin = null;
Reader chartIn = null;
try
{
chartIn = IOUtil.getReader(chartFile, this.encoding);
JsDefContent jsDefContent = this.htmlChartPluginJsDefResolver.resolve(chartIn);
if (!StringUtil.isEmpty(jsDefContent.getPluginJson())
&& !StringUtil.isEmpty(jsDefContent.getPluginChartRenderer()))
{
plugin = createHtmlChartPlugin();
this.jsonChartPluginPropertiesResolver.resolveChartPluginProperties(plugin, jsDefContent.getPluginJson());
plugin.setChartRenderer(new StringJsChartRenderer(jsDefContent.getPluginChartRenderer()));
plugin.setIcons(toBytesIconsInDirectory(directory, plugin.getIcons()));
if (StringUtil.isEmpty(plugin.getId()) || StringUtil.isEmpty(plugin.getNameLabel()))
plugin = null;
}
}
catch (Exception e)
{
throw new HtmlChartPluginLoadException(e);
}
finally
{
IOUtil.close(chartIn);
}
// 设置为加载时间而不取文件上次修改时间因为文件上次修改时间可能错乱
if (plugin != null)
plugin.setLastModified(System.currentTimeMillis());
return plugin;
}
protected Map<RenderStyle, Icon> toBytesIconsInDirectory(File directory, Map<RenderStyle, Icon> icons)
throws IOException
{

View File

@ -484,7 +484,7 @@ public class HtmlTplDashboardWidgetHtmlRenderer<T extends HtmlRenderContext> ext
if (chartInfos != null)
{
List<HtmlChartWidget<HtmlRenderContext>> chartWidgets = getHtmlChartWidgets(renderContext, chartInfos);
List<String> chartPluginVarNames = writeHtmlChartPluginScripts(renderContext, chartWidgets);
List<String> chartPluginVarNames = writeHtmlChartPluginScriptsResolveImport(renderContext, chartWidgets);
HtmlChartPluginRenderOption option = new HtmlChartPluginRenderOption();
option.setNotWriteChartElement(true);
@ -517,42 +517,6 @@ public class HtmlTplDashboardWidgetHtmlRenderer<T extends HtmlRenderContext> ext
}
}
protected List<String> writeHtmlChartPluginScripts(T renderContext,
List<HtmlChartWidget<HtmlRenderContext>> htmlChartWidgets) throws IOException
{
List<String> pluginVarNames = new ArrayList<>(htmlChartWidgets.size());
for (int i = 0; i < htmlChartWidgets.size(); i++)
{
String pluginVarName = null;
HtmlChartWidget<HtmlRenderContext> widget = htmlChartWidgets.get(i);
HtmlChartPlugin<HtmlRenderContext> plugin = widget.getPlugin();
for (int j = 0; j < i; j++)
{
HtmlChartWidget<HtmlRenderContext> myWidget = htmlChartWidgets.get(j);
HtmlChartPlugin<HtmlRenderContext> myPlugin = myWidget.getPlugin();
if (myPlugin.getId().equals(plugin.getId()))
{
pluginVarName = pluginVarNames.get(j);
break;
}
}
if (pluginVarName == null)
{
pluginVarName = HtmlRenderAttributes.generateChartPluginVarName(renderContext);
getHtmlChartPluginScriptObjectWriter().write(renderContext.getWriter(), plugin, pluginVarName);
}
pluginVarNames.add(pluginVarName);
}
return pluginVarNames;
}
protected List<HtmlChartWidget<HtmlRenderContext>> getHtmlChartWidgets(HtmlRenderContext renderContext,
List<ChartInfo> chartInfos)
{

View File

@ -163,6 +163,8 @@ public abstract class HtmlTplDashboardWidgetRenderer<T extends HtmlRenderContext
/** 默认看板变量名 */
private String defaultDashboardVar = DEFAULT_DASHBOARD_VAR;
private ImportHtmlChartPluginVarNameResolver importHtmlChartPluginVarNameResolver;
/** 换行符 */
private String newLine = HtmlChartPlugin.HTML_NEW_LINE;
@ -395,6 +397,17 @@ public abstract class HtmlTplDashboardWidgetRenderer<T extends HtmlRenderContext
this.defaultDashboardVar = defaultDashboardVar;
}
public ImportHtmlChartPluginVarNameResolver getImportHtmlChartPluginVarNameResolver()
{
return importHtmlChartPluginVarNameResolver;
}
public void setImportHtmlChartPluginVarNameResolver(
ImportHtmlChartPluginVarNameResolver importHtmlChartPluginVarNameResolver)
{
this.importHtmlChartPluginVarNameResolver = importHtmlChartPluginVarNameResolver;
}
public String getNewLine()
{
return newLine;
@ -976,6 +989,79 @@ public abstract class HtmlTplDashboardWidgetRenderer<T extends HtmlRenderContext
}
}
/**
* {@linkplain HtmlChartPlugin} JS脚本并返回对应的变量名列表
* <p>
* 如果{@linkplain #getImportHtmlChartPluginVarNameResolver()}不为{@code null}此方法不会写{@linkplain HtmlChartPlugin}
* JS脚本而仅返回导入变量名列表
* </p>
*
* @param renderContext
* @param htmlChartWidgets
* @return
* @throws IOException
*/
protected List<String> writeHtmlChartPluginScriptsResolveImport(T renderContext,
List<HtmlChartWidget<HtmlRenderContext>> htmlChartWidgets) throws IOException
{
if(this.importHtmlChartPluginVarNameResolver == null)
return writeHtmlChartPluginScripts(renderContext, htmlChartWidgets);
List<String> pluginVarNames = new ArrayList<>(htmlChartWidgets.size());
for (HtmlChartWidget<?> widget : htmlChartWidgets)
{
String importVarName = this.importHtmlChartPluginVarNameResolver.resolve(widget);
pluginVarNames.add(importVarName);
}
return pluginVarNames;
}
/**
* {@linkplain HtmlChartPlugin} JS脚本并返回对应的变量名列表
*
* @param renderContext
* @param htmlChartWidgets
* @return
* @throws IOException
*/
protected List<String> writeHtmlChartPluginScripts(T renderContext,
List<HtmlChartWidget<HtmlRenderContext>> htmlChartWidgets) throws IOException
{
List<String> pluginVarNames = new ArrayList<>(htmlChartWidgets.size());
for (int i = 0; i < htmlChartWidgets.size(); i++)
{
String pluginVarName = null;
HtmlChartWidget<HtmlRenderContext> widget = htmlChartWidgets.get(i);
HtmlChartPlugin<HtmlRenderContext> plugin = widget.getPlugin();
for (int j = 0; j < i; j++)
{
HtmlChartWidget<HtmlRenderContext> myWidget = htmlChartWidgets.get(j);
HtmlChartPlugin<HtmlRenderContext> myPlugin = myWidget.getPlugin();
if (myPlugin.getId().equals(plugin.getId()))
{
pluginVarName = pluginVarNames.get(j);
break;
}
}
if (pluginVarName == null)
{
pluginVarName = HtmlRenderAttributes.generateChartPluginVarName(renderContext);
getHtmlChartPluginScriptObjectWriter().write(renderContext.getWriter(), plugin, pluginVarName);
}
pluginVarNames.add(pluginVarName);
}
return pluginVarNames;
}
/**
* {@linkplain HtmlChart}
*
@ -1443,4 +1529,64 @@ public abstract class HtmlTplDashboardWidgetRenderer<T extends HtmlRenderContext
return this.prefix + rawTitle;
}
}
/**
* 引入{@linkplain HtmlChartPlugin}变量名处理器
* <p>
* 为了避免每次渲染{@linkplain HtmlTplDashboard}时都内联渲染{@linkplain HtmlChartPlugin}对象
* 通常会将所有{@linkplain HtmlChartPlugin}对象独立渲染而在渲染{@linkplain HtmlTplDashboard}时引入
* 此类即为此提供支持用于获取引入{@linkplain HtmlChartPlugin}对象的变量名
* </p>
*
* @author datagear@163.com
*
*/
public static interface ImportHtmlChartPluginVarNameResolver
{
/**
* 获取引入变量名
* <p>
* 例如<code>"chartPluginManager.get('...')"</code>
* </p>
*
* @param chartWidget
* @return
*/
String resolve(HtmlChartWidget<?> chartWidget);
}
public static class TemplateImportHtmlChartPluginVarNameResolver implements ImportHtmlChartPluginVarNameResolver
{
public static final String PLACEHOLDER_CHART_PLUGIN_ID = "$CHART_PLUGIN_ID";
private String template;
public TemplateImportHtmlChartPluginVarNameResolver()
{
super();
}
public TemplateImportHtmlChartPluginVarNameResolver(String template)
{
super();
this.template = template;
}
public String getTemplate()
{
return template;
}
public void setTemplate(String template)
{
this.template = template;
}
@Override
public String resolve(HtmlChartWidget<?> chartWidget)
{
String chartPluginId = chartWidget.getPlugin().getId();
return this.template.replace(PLACEHOLDER_CHART_PLUGIN_ID, chartPluginId);
}
}
}

View File

@ -45,6 +45,8 @@ public abstract class AbstractController
public static final String CONTENT_TYPE_CSS = "text/css;";
public static final String CONTENT_TYPE_JAVASCRIPT = "application/javascript;";
public static final String KEY_TITLE_MESSAGE_KEY = "titleMessageKey";
public static final String KEY_FORM_ACTION = "formAction";

View File

@ -8,6 +8,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -26,6 +27,8 @@ import org.datagear.analysis.support.CategorizationResolver.Categorization;
import org.datagear.analysis.support.html.HtmlChartPlugin;
import org.datagear.analysis.support.html.HtmlChartPluginLoadException;
import org.datagear.analysis.support.html.HtmlChartPluginLoader;
import org.datagear.analysis.support.html.HtmlChartPluginScriptObjectWriter;
import org.datagear.analysis.support.html.HtmlRenderContext;
import org.datagear.persistence.PagingQuery;
import org.datagear.util.FileUtil;
import org.datagear.util.IOUtil;
@ -56,6 +59,8 @@ public class ChartPluginController extends AbstractChartPluginAwareController
@Autowired
private File tempDirectory;
private HtmlChartPluginScriptObjectWriter htmlChartPluginScriptObjectWriter = new HtmlChartPluginScriptObjectWriter();
public ChartPluginController()
{
super();
@ -71,6 +76,17 @@ public class ChartPluginController extends AbstractChartPluginAwareController
this.tempDirectory = tempDirectory;
}
public HtmlChartPluginScriptObjectWriter getHtmlChartPluginScriptObjectWriter()
{
return htmlChartPluginScriptObjectWriter;
}
public void setHtmlChartPluginScriptObjectWriter(
HtmlChartPluginScriptObjectWriter htmlChartPluginScriptObjectWriter)
{
this.htmlChartPluginScriptObjectWriter = htmlChartPluginScriptObjectWriter;
}
@RequestMapping("/upload")
public String upload(HttpServletRequest request, org.springframework.ui.Model model)
{
@ -257,4 +273,59 @@ public class ChartPluginController extends AbstractChartPluginAwareController
IOUtil.close(iconIn);
}
}
@RequestMapping("/chartPluginManager.js")
public void getChartPluginManagerJs(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest)
throws Exception
{
List<ChartPlugin<HtmlRenderContext>> plugins = getDirectoryHtmlChartPluginManager()
.getAll(HtmlRenderContext.class);
List<HtmlChartPlugin<?>> htmlChartPlugins = new ArrayList<HtmlChartPlugin<?>>(plugins.size());
long lastModified = -1;
if (plugins != null)
{
for (ChartPlugin<HtmlRenderContext> plugin : plugins)
{
if (plugin instanceof HtmlChartPlugin<?>)
{
HtmlChartPlugin<?> htmlChartPlugin = (HtmlChartPlugin<?>) plugin;
htmlChartPlugins.add(htmlChartPlugin);
lastModified = Math.max(lastModified, htmlChartPlugin.getLastModified());
}
}
}
if (webRequest.checkNotModified(lastModified))
return;
response.setContentType(CONTENT_TYPE_JAVASCRIPT);
PrintWriter out = response.getWriter();
out.println("(function(global)");
out.println("{");
out.println("var chartPluginManager = (global.chartPluginManager || (global.chartPluginManager = {}));");
out.println("chartPluginManager.plugins = (chartPluginManager.plugins || {});");
out.println();
out.println("chartPluginManager.get = function(id){ return this.plugins[id]; };");
out.println();
for (int i = 0, len = htmlChartPlugins.size(); i < len; i++)
{
HtmlChartPlugin<?> plugin = htmlChartPlugins.get(i);
String pluginVar = "plugin" + i;
this.htmlChartPluginScriptObjectWriter.write(out, plugin, pluginVar);
out.println("chartPluginManager.plugins[\"" + WebUtils.escapeJavaScriptStringValue(plugin.getId())
+ "\"] = " + pluginVar + ";");
}
out.println("})(this);");
}
}

View File

@ -467,4 +467,40 @@ public class WebUtils
return defaultValue;
}
/**
* 转换为JavaScript语法的字符串
*
* @param s
* @return
*/
public static String escapeJavaScriptStringValue(String s)
{
if (s == null)
return "";
StringBuilder sb = new StringBuilder();
char[] cs = s.toCharArray();
for (char c : cs)
{
if (c == '\\')
sb.append("\\\\");
else if (c == '\'')
sb.append("\\\'");
else if (c == '"')
sb.append("\\\"");
else if (c == '\t')
sb.append("\\\t");
else if (c == '\n')
sb.append("\\\n");
else if (c == '\r')
sb.append("\\\r");
else
sb.append(c);
}
return sb.toString();
}
}

View File

@ -166,6 +166,10 @@
<property name="name" value="echarts-wordcloud" />
<property name="content" value="&lt;script type='text/javascript' res-name='echarts-wordcloud' src='$CONTEXTPATH/static/script/echarts-wordcloud-1.1.2/echarts-wordcloud.min.js?v=$VERSION'&gt;&lt;/script&gt;" />
</bean>
<bean class="org.datagear.analysis.support.html.HtmlTplDashboardImport">
<property name="name" value="chartPluginManager" />
<property name="content" value="&lt;script type='text/javascript' res-name='chartPluginManager' src='$CONTEXTPATH/analysis/chartPlugin/chartPluginManager.js?v=$VERSION'&gt;&lt;/script&gt;" />
</bean>
<bean class="org.datagear.analysis.support.html.HtmlTplDashboardImport">
<property name="name" value="chartFactory" />
<property name="content" value="&lt;script type='text/javascript' res-name='chartFactory' src='$CONTEXTPATH/static/script/datagear-chartFactory.js?v=$VERSION'&gt;&lt;/script&gt;" />
@ -202,6 +206,11 @@
</property>
<property name="templateDashboardWidgetResManager" ref="templateDashboardWidgetResManager" />
<property name="chartWidgetSource" ref="htmlChartWidgetEntityService" />
<property name="importHtmlChartPluginVarNameResolver">
<bean class="org.datagear.analysis.support.html.HtmlTplDashboardWidgetRenderer.TemplateImportHtmlChartPluginVarNameResolver">
<property name="template" value="chartPluginManager.get('$CHART_PLUGIN_ID')" />
</bean>
</property>
</bean>
<bean id="sqlDataSetEntityService" class="org.datagear.management.service.impl.SqlDataSetEntityServiceImpl">

View File

@ -57,6 +57,7 @@
<intercept-url pattern="${subContextPath}/analysis/chartPlugin/select" access="IS_AUTHENTICATED_ANONYMOUSLY,ROLE_USER" />
<intercept-url pattern="${subContextPath}/analysis/chartPlugin/selectData" access="IS_AUTHENTICATED_ANONYMOUSLY,ROLE_USER" />
<intercept-url pattern="${subContextPath}/analysis/chartPlugin/icon/*" access="IS_AUTHENTICATED_ANONYMOUSLY,ROLE_USER" />
<intercept-url pattern="${subContextPath}/analysis/chartPlugin/chartPluginManager.js" access="IS_AUTHENTICATED_ANONYMOUSLY,ROLE_USER" />
<intercept-url pattern="${subContextPath}/analysis/chartPlugin/**" access="ROLE_ADMIN" />
<intercept-url pattern="${subContextPath}/login/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />