forked from fanruan/design
Browse Source
* commit 'c8e3f2eebbbf2532db84cd3737820b0f8a291e6e': REPORT-14865 更新日志推送=>跳过推送版本&修复单元测试错误 REPORT-14865 更新日志推送=>调整代码 REPORT-14865 更新日志推送=>调整代码 REPORT-14865 更新日志推送=>改用 withComponent 方法 REPORT-14865 更新日志推送=>修改包名 REPORT-14865 更新日志推送=>跳转更新 REPORT-14865 更新日志推送=>调整代码 REPORT-14865 更新日志推送=>国际化 REPORT-14865 更新日志推送=>调整代码 REPORT-14865 更新日志推送(部分) REPORT-14865 更新日志推送=>ModernUIPane 传一份到 feature REPORT-14865 更新日志推送=>实现弹窗逻辑的核心类 REPORT-14865 更新日志推送=>持久化,重构 REPORT-14865 更新日志推送=>“自动推送更新”开关research/10.0
47 changed files with 1871 additions and 51 deletions
@ -0,0 +1,53 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.teamdev.jxbrowser.chromium.Browser; |
||||||
|
import com.teamdev.jxbrowser.chromium.BrowserContext; |
||||||
|
import com.teamdev.jxbrowser.chromium.ProtocolService; |
||||||
|
import com.teamdev.jxbrowser.chromium.URLResponse; |
||||||
|
|
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-07 |
||||||
|
*/ |
||||||
|
public class Assistant { |
||||||
|
|
||||||
|
public static URLResponse inputStream2Response(InputStream inputStream, String filePath) throws Exception { |
||||||
|
URLResponse response = new URLResponse(); |
||||||
|
DataInputStream stream = new DataInputStream(inputStream); |
||||||
|
byte[] data = new byte[stream.available()]; |
||||||
|
stream.readFully(data); |
||||||
|
response.setData(data); |
||||||
|
String mimeType = getMimeType(filePath); |
||||||
|
response.getHeaders().setHeader("Content-Type", mimeType); |
||||||
|
return response; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private static String getMimeType(String path) { |
||||||
|
if (StringUtils.isBlank(path)) { |
||||||
|
return "text/html"; |
||||||
|
} |
||||||
|
if (path.endsWith(".html")) { |
||||||
|
return "text/html"; |
||||||
|
} |
||||||
|
if (path.endsWith(".css")) { |
||||||
|
return "text/css"; |
||||||
|
} |
||||||
|
if (path.endsWith(".js")) { |
||||||
|
return "text/javascript"; |
||||||
|
} |
||||||
|
return "text/html"; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setEmbProtocolHandler(Browser browser, EmbProtocolHandler handler) { |
||||||
|
BrowserContext browserContext = browser.getContext(); |
||||||
|
ProtocolService protocolService = browserContext.getProtocolService(); |
||||||
|
// 支持读取jar包中文件的自定义协议————emb:/com/fr/design/images/bbs.png
|
||||||
|
protocolService.setProtocolHandler("emb", handler); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.web.struct.AssembleComponent; |
||||||
|
import com.fr.web.struct.AtomBuilder; |
||||||
|
import com.fr.web.struct.PathGroup; |
||||||
|
import com.fr.web.struct.category.ScriptPath; |
||||||
|
import com.fr.web.struct.category.StylePath; |
||||||
|
import com.teamdev.jxbrowser.chromium.ProtocolHandler; |
||||||
|
import com.teamdev.jxbrowser.chromium.URLRequest; |
||||||
|
import com.teamdev.jxbrowser.chromium.URLResponse; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-07 |
||||||
|
*/ |
||||||
|
public class EmbProtocolHandler implements ProtocolHandler { |
||||||
|
|
||||||
|
private AssembleComponent component; |
||||||
|
|
||||||
|
public EmbProtocolHandler() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public EmbProtocolHandler(AssembleComponent component) { |
||||||
|
this.component = component; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public URLResponse onRequest(URLRequest req) { |
||||||
|
try { |
||||||
|
String path = req.getURL(); |
||||||
|
if (path.startsWith("emb:dynamic")) { |
||||||
|
URLResponse response = new URLResponse(); |
||||||
|
response.setData(htmlText().getBytes()); |
||||||
|
response.getHeaders().setHeader("Content-Type", "text/html"); |
||||||
|
return response; |
||||||
|
} else { |
||||||
|
int index = path.indexOf("="); |
||||||
|
if (index > 0) { |
||||||
|
path = path.substring(index + 1); |
||||||
|
} else { |
||||||
|
path = path.substring(4); |
||||||
|
} |
||||||
|
InputStream inputStream = IOUtils.readResource(path); |
||||||
|
return Assistant.inputStream2Response(inputStream, path); |
||||||
|
} |
||||||
|
} catch (Exception ignore) { |
||||||
|
|
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private String htmlText() { |
||||||
|
PathGroup pathGroup = AtomBuilder.create().buildAssembleFilePath(ModernRequestClient.KEY, component); |
||||||
|
StylePath[] stylePaths = pathGroup.toStylePathGroup(); |
||||||
|
StringBuilder styleText = new StringBuilder(); |
||||||
|
for (StylePath path : stylePaths) { |
||||||
|
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||||
|
styleText.append("<link rel=\"stylesheet\" href=\"emb:"); |
||||||
|
styleText.append(path.toFilePath()); |
||||||
|
styleText.append("\"/>"); |
||||||
|
} |
||||||
|
} |
||||||
|
String result = ModernUIConstants.HTML_TPL.replaceAll("##style##", styleText.toString()); |
||||||
|
ScriptPath[] scriptPaths = pathGroup.toScriptPathGroup(); |
||||||
|
StringBuilder scriptText = new StringBuilder(); |
||||||
|
for (ScriptPath path : scriptPaths) { |
||||||
|
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||||
|
scriptText.append("<script src=\"emb:"); |
||||||
|
scriptText.append(path.toFilePath()); |
||||||
|
scriptText.append("\"></script>"); |
||||||
|
} |
||||||
|
} |
||||||
|
result = result.replaceAll("##script##", scriptText.toString()); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.web.struct.browser.RequestClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-07 |
||||||
|
*/ |
||||||
|
public enum ModernRequestClient implements RequestClient { |
||||||
|
|
||||||
|
KEY; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isIE() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isLowIEVersion() { |
||||||
|
return false; |
||||||
|
}} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.general.IOUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-05 |
||||||
|
*/ |
||||||
|
class ModernUIConstants { |
||||||
|
|
||||||
|
static final String SCRIPT_INIT_NAME_SPACE = IOUtils.readResourceAsString("/com/fr/design/ui/InitNameSpace.js"); |
||||||
|
|
||||||
|
static final String HTML_TPL = IOUtils.readResourceAsString("/com/fr/design/ui/tpl.html"); |
||||||
|
} |
@ -0,0 +1,180 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.dialog.BasicPane; |
||||||
|
import com.fr.web.struct.AssembleComponent; |
||||||
|
import com.teamdev.jxbrowser.chromium.Browser; |
||||||
|
import com.teamdev.jxbrowser.chromium.BrowserPreferences; |
||||||
|
import com.teamdev.jxbrowser.chromium.JSValue; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.FinishLoadingEvent; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.LoadAdapter; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.LoadListener; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; |
||||||
|
import com.teamdev.jxbrowser.chromium.events.ScriptContextListener; |
||||||
|
import com.teamdev.jxbrowser.chromium.swing.BrowserView; |
||||||
|
|
||||||
|
import javax.swing.JSplitPane; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-04 |
||||||
|
* 用于加载html5的Swing容器,可以在设计选项设置中打开调试窗口,示例可查看:com.fr.design.ui.ModernUIPaneTest |
||||||
|
*/ |
||||||
|
public class ModernUIPane<T> extends BasicPane { |
||||||
|
|
||||||
|
private Browser browser; |
||||||
|
private String namespace = "Pool"; |
||||||
|
private String variable = "data"; |
||||||
|
private String expression = "update()"; |
||||||
|
|
||||||
|
private ModernUIPane() { |
||||||
|
initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initialize() { |
||||||
|
if (browser == null) { |
||||||
|
setLayout(new BorderLayout()); |
||||||
|
BrowserPreferences.setChromiumSwitches("--disable-google-traffic"); |
||||||
|
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||||
|
JSplitPane splitPane = new JSplitPane(); |
||||||
|
add(splitPane, BorderLayout.CENTER); |
||||||
|
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); |
||||||
|
splitPane.setDividerLocation(500); |
||||||
|
BrowserPreferences.setChromiumSwitches("--remote-debugging-port=9222"); |
||||||
|
initializeBrowser(); |
||||||
|
splitPane.setLeftComponent(new BrowserView(browser)); |
||||||
|
Browser debugger = new Browser(); |
||||||
|
debugger.loadURL(browser.getRemoteDebuggingURL()); |
||||||
|
BrowserView debuggerView = new BrowserView(debugger); |
||||||
|
splitPane.setRightComponent(debuggerView); |
||||||
|
} else { |
||||||
|
initializeBrowser(); |
||||||
|
add(new BrowserView(browser), BorderLayout.CENTER); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initializeBrowser() { |
||||||
|
browser = new Browser(); |
||||||
|
// 初始化的时候,就把命名空间对象初始化好,确保window.a.b.c("a.b.c"为命名空间)对象都是初始化过的
|
||||||
|
browser.addScriptContextListener(new ScriptContextAdapter() { |
||||||
|
@Override |
||||||
|
public void onScriptContextCreated(ScriptContextEvent event) { |
||||||
|
event.getBrowser().executeJavaScript(String.format(ModernUIConstants.SCRIPT_INIT_NAME_SPACE, namespace)); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String title4PopupWindow() { |
||||||
|
return "Modern"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void populate(final T t) { |
||||||
|
browser.addScriptContextListener(new ScriptContextAdapter() { |
||||||
|
@Override |
||||||
|
public void onScriptContextCreated(ScriptContextEvent event) { |
||||||
|
JSValue ns = event.getBrowser().executeJavaScriptAndReturnValue("window." + namespace); |
||||||
|
ns.asObject().setProperty(variable, t); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public T update() { |
||||||
|
JSValue jsValue = browser.executeJavaScriptAndReturnValue("window." + namespace + "." + expression); |
||||||
|
if (jsValue.isObject()) { |
||||||
|
return (T)jsValue.asJavaObject(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public static class Builder<T> { |
||||||
|
|
||||||
|
private ModernUIPane<T> pane = new ModernUIPane<>(); |
||||||
|
|
||||||
|
public Builder<T> prepare(ScriptContextListener contextListener) { |
||||||
|
pane.browser.addScriptContextListener(contextListener); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder<T> prepare(LoadListener loadListener) { |
||||||
|
pane.browser.addLoadListener(loadListener); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载jar包中的资源 |
||||||
|
* @param path 资源路径 |
||||||
|
*/ |
||||||
|
public Builder<T> withEMB(final String path) { |
||||||
|
Assistant.setEmbProtocolHandler(pane.browser, new EmbProtocolHandler()); |
||||||
|
pane.browser.loadURL("emb:" + path); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载url指向的资源 |
||||||
|
* @param url 文件的地址 |
||||||
|
*/ |
||||||
|
public Builder<T> withURL(final String url) { |
||||||
|
Assistant.setEmbProtocolHandler(pane.browser, new EmbProtocolHandler()); |
||||||
|
pane.browser.loadURL(url); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载Atom组件 |
||||||
|
* @param component Atom组件 |
||||||
|
*/ |
||||||
|
public Builder<T> withComponent(AssembleComponent component) { |
||||||
|
Assistant.setEmbProtocolHandler(pane.browser, new EmbProtocolHandler(component)); |
||||||
|
pane.browser.loadURL("emb:dynamic"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载html文本内容 |
||||||
|
* @param html 要加载html文本内容 |
||||||
|
*/ |
||||||
|
public Builder<T> withHTML(String html) { |
||||||
|
Assistant.setEmbProtocolHandler(pane.browser, new EmbProtocolHandler()); |
||||||
|
pane.browser.loadHTML(html); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置该前端页面做数据交换所使用的对象 |
||||||
|
* @param namespace 对象名 |
||||||
|
*/ |
||||||
|
public Builder<T> namespace(String namespace) { |
||||||
|
pane.namespace = namespace; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* java端往js端传数据时使用的变量名字 |
||||||
|
* @param name 变量的名字 |
||||||
|
*/ |
||||||
|
public Builder<T> variable(String name) { |
||||||
|
pane.variable = name; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* js端往java端传数据时执行的函数表达式 |
||||||
|
* @param expression 函数表达式 |
||||||
|
*/ |
||||||
|
public Builder<T> expression(String expression) { |
||||||
|
pane.expression = expression; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ModernUIPane<T> build() { |
||||||
|
return pane; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,9 +1,9 @@ |
|||||||
package com.fr.design.onlineupdate.actions; |
package com.fr.design.update.actions; |
||||||
|
|
||||||
import com.fr.design.onlineupdate.domain.UpdateConstants; |
import com.fr.design.update.domain.UpdateConstants; |
||||||
import com.fr.locale.InterProviderFactory; |
import com.fr.locale.InterProviderFactory; |
||||||
import com.fr.log.FineLoggerFactory; |
import com.fr.log.FineLoggerFactory; |
||||||
import com.fr.design.onlineupdate.domain.DownloadItem; |
import com.fr.design.update.domain.DownloadItem; |
||||||
import com.fr.stable.ArrayUtils; |
import com.fr.stable.ArrayUtils; |
||||||
import com.fr.stable.StableUtils; |
import com.fr.stable.StableUtils; |
||||||
|
|
@ -1,10 +1,9 @@ |
|||||||
package com.fr.design.onlineupdate.actions; |
package com.fr.design.update.actions; |
||||||
|
|
||||||
import com.fr.base.BaseUtils; |
import com.fr.base.BaseUtils; |
||||||
import com.fr.design.actions.UpdateAction; |
import com.fr.design.actions.UpdateAction; |
||||||
import com.fr.design.mainframe.DesignerContext; |
import com.fr.design.mainframe.DesignerContext; |
||||||
import com.fr.design.onlineupdate.ui.dialog.UpdateMainDialog; |
import com.fr.design.update.ui.dialog.UpdateMainDialog; |
||||||
import com.fr.locale.InterProviderFactory; |
|
||||||
|
|
||||||
import java.awt.event.ActionEvent; |
import java.awt.event.ActionEvent; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.domain; |
package com.fr.design.update.domain; |
||||||
|
|
||||||
import com.fr.general.ComparatorUtils; |
import com.fr.general.ComparatorUtils; |
||||||
import com.fr.json.JSONObject; |
import com.fr.json.JSONObject; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.domain; |
package com.fr.design.update.domain; |
||||||
|
|
||||||
/** |
/** |
||||||
* Created by XINZAI on 2018/8/21. |
* Created by XINZAI on 2018/8/21. |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.domain; |
package com.fr.design.update.domain; |
||||||
|
|
||||||
import com.fr.log.FineLoggerFactory; |
import com.fr.log.FineLoggerFactory; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.factory; |
package com.fr.design.update.factory; |
||||||
|
|
||||||
import com.fr.log.FineLoggerFactory; |
import com.fr.log.FineLoggerFactory; |
||||||
import com.fr.stable.ArrayUtils; |
import com.fr.stable.ArrayUtils; |
@ -0,0 +1,61 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLReadable; |
||||||
|
import com.fr.stable.xml.XMLWriter; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
|
||||||
|
/** |
||||||
|
* 持久化与设计器自动推送更新相关的配置 |
||||||
|
* Created by plough on 2019/4/8. |
||||||
|
*/ |
||||||
|
public class DesignerPushUpdateConfigManager implements XMLReadable, XMLWriter { |
||||||
|
public static final String XML_TAG = "DesignerPushUpdateConfigManager"; |
||||||
|
private static DesignerPushUpdateConfigManager singleton; |
||||||
|
|
||||||
|
private boolean autoPushUpdateEnabled = true; // 是否开启自动推送更新
|
||||||
|
private String lastIgnoredVersion = StringUtils.EMPTY; // 最近一次跳过的更新版本
|
||||||
|
|
||||||
|
private DesignerPushUpdateConfigManager() { |
||||||
|
} |
||||||
|
|
||||||
|
public static DesignerPushUpdateConfigManager getInstance() { |
||||||
|
if (singleton == null) { |
||||||
|
singleton = new DesignerPushUpdateConfigManager(); |
||||||
|
} |
||||||
|
return singleton; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readXML(XMLableReader reader) { |
||||||
|
if (reader.isAttr()) { |
||||||
|
this.setAutoPushUpdateEnabled(reader.getAttrAsBoolean("autoPushUpdateEnabled", true)); |
||||||
|
this.setLastIgnoredVersion(reader.getAttrAsString("lastIgnoredVersion", StringUtils.EMPTY)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeXML(XMLPrintWriter writer) { |
||||||
|
writer.startTAG(XML_TAG); |
||||||
|
writer.attr("autoPushUpdateEnabled", autoPushUpdateEnabled); |
||||||
|
writer.attr("lastIgnoredVersion", lastIgnoredVersion); |
||||||
|
writer.end(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isAutoPushUpdateEnabled() { |
||||||
|
return autoPushUpdateEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAutoPushUpdateEnabled(boolean autoPushUpdateEnabled) { |
||||||
|
this.autoPushUpdateEnabled = autoPushUpdateEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLastIgnoredVersion() { |
||||||
|
return lastIgnoredVersion; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLastIgnoredVersion(String lastIgnoredVersion) { |
||||||
|
this.lastIgnoredVersion = lastIgnoredVersion; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.design.dialog.UIDialog; |
||||||
|
import com.fr.design.ui.ModernUIPane; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.web.struct.AssembleComponent; |
||||||
|
import com.fr.web.struct.Atom; |
||||||
|
import com.fr.web.struct.browser.RequestClient; |
||||||
|
import com.fr.web.struct.category.ScriptPath; |
||||||
|
import com.fr.web.struct.category.StylePath; |
||||||
|
import com.fr.web.struct.impl.FineUI; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Frame; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/10. |
||||||
|
*/ |
||||||
|
class DesignerPushUpdateDialog extends UIDialog { |
||||||
|
public static final Dimension DEFAULT = new Dimension(640, 320); |
||||||
|
|
||||||
|
private ModernUIPane<Model> jsPane; |
||||||
|
|
||||||
|
private DesignerPushUpdateDialog(Frame parent) { |
||||||
|
super(parent); |
||||||
|
setModal(true); |
||||||
|
initComponents(); |
||||||
|
} |
||||||
|
|
||||||
|
static void createAndShow(Frame parent, DesignerUpdateInfo updateInfo) { |
||||||
|
DesignerPushUpdateDialog dialog = new DesignerPushUpdateDialog(parent); |
||||||
|
dialog.populate(updateInfo); |
||||||
|
dialog.showDialog(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponents() { |
||||||
|
JPanel contentPane = (JPanel) getContentPane(); |
||||||
|
contentPane.setLayout(new BorderLayout()); |
||||||
|
|
||||||
|
jsPane = new ModernUIPane.Builder<Model>() |
||||||
|
.withComponent(new AssembleComponent() { |
||||||
|
@Override |
||||||
|
public ScriptPath script(RequestClient req) { |
||||||
|
return ScriptPath.build("/com/fr/design/ui/update/push/pushUpdate.js"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StylePath style(RequestClient req) { |
||||||
|
return StylePath.build("/com/fr/design/ui/update/push/pushUpdate.css"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Atom[] refer() { |
||||||
|
return new Atom[]{FineUI.KEY}; |
||||||
|
} |
||||||
|
}).namespace("Pool").build(); |
||||||
|
|
||||||
|
contentPane.add(jsPane); |
||||||
|
} |
||||||
|
|
||||||
|
private void populate(DesignerUpdateInfo updateInfo) { |
||||||
|
Model model = createModel(updateInfo); |
||||||
|
jsPane.populate(model); |
||||||
|
} |
||||||
|
|
||||||
|
private Model createModel(DesignerUpdateInfo updateInfo) { |
||||||
|
Model model = new Model(); |
||||||
|
model.setVersion(updateInfo.getPushVersion()); |
||||||
|
model.setContent(updateInfo.getPushContent()); |
||||||
|
model.setMoreInfoUrl(updateInfo.getMoreInfoUrl()); |
||||||
|
model.setBackgroundUrl(updateInfo.getBackgroundUrl()); |
||||||
|
return model; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void checkValid() throws Exception { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示窗口 |
||||||
|
*/ |
||||||
|
private void showDialog() { |
||||||
|
setSize(DEFAULT); |
||||||
|
setUndecorated(true); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
public class Model { |
||||||
|
private String version; |
||||||
|
private String content; |
||||||
|
private String moreInfoUrl; |
||||||
|
private String backgroundUrl; |
||||||
|
|
||||||
|
public String getVersion() { |
||||||
|
return version; |
||||||
|
} |
||||||
|
|
||||||
|
public void setVersion(String version) { |
||||||
|
this.version = version; |
||||||
|
} |
||||||
|
|
||||||
|
public String getContent() { |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
public void setContent(String content) { |
||||||
|
this.content = content; |
||||||
|
} |
||||||
|
|
||||||
|
public String getMoreInfoUrl() { |
||||||
|
return moreInfoUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public void setMoreInfoUrl(String moreInfoUrl) { |
||||||
|
this.moreInfoUrl = moreInfoUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public String getBackgroundUrl() { |
||||||
|
return backgroundUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public void setBackgroundUrl(String backgroundUrl) { |
||||||
|
this.backgroundUrl = backgroundUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public void updateNow() { |
||||||
|
DesignerPushUpdateManager.getInstance().doUpdate(); |
||||||
|
exit(); |
||||||
|
} |
||||||
|
|
||||||
|
public void remindNextTime() { |
||||||
|
exit(); |
||||||
|
} |
||||||
|
|
||||||
|
public void skipThisVersion() { |
||||||
|
DesignerPushUpdateManager.getInstance().skipCurrentPushVersion(); |
||||||
|
exit(); |
||||||
|
} |
||||||
|
|
||||||
|
public String i18nText(String key) { |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText(key); |
||||||
|
} |
||||||
|
|
||||||
|
private void exit() { |
||||||
|
DesignerPushUpdateDialog.this.dialogExit(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,174 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.design.event.DesignerOpenedListener; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrame; |
||||||
|
import com.fr.design.update.ui.dialog.UpdateMainDialog; |
||||||
|
import com.fr.general.CloudCenter; |
||||||
|
import com.fr.general.GeneralContext; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.general.http.HttpToolbox; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/8. |
||||||
|
*/ |
||||||
|
public class DesignerPushUpdateManager { |
||||||
|
private static final String SPLIT_CHAR = "-"; |
||||||
|
private static DesignerPushUpdateManager singleton; |
||||||
|
private DesignerUpdateInfo updateInfo; |
||||||
|
private DesignerPushUpdateConfigManager config; |
||||||
|
|
||||||
|
static { |
||||||
|
DesignerContext.getDesignerFrame().addDesignerOpenedListener(new DesignerOpenedListener() { |
||||||
|
@Override |
||||||
|
public void designerOpened() { |
||||||
|
getInstance().checkAndPop(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private DesignerPushUpdateManager() { |
||||||
|
config = DesignerPushUpdateConfigManager.getInstance(); |
||||||
|
} |
||||||
|
|
||||||
|
public static DesignerPushUpdateManager getInstance() { |
||||||
|
if (singleton == null) { |
||||||
|
singleton = new DesignerPushUpdateManager(); |
||||||
|
} |
||||||
|
return singleton; |
||||||
|
} |
||||||
|
|
||||||
|
private void initUpdateInfo(String currentVersion, String latestVersion) { |
||||||
|
String lastIgnoredVersion = config.getLastIgnoredVersion(); |
||||||
|
String updatePushInfo = CloudCenter.getInstance().acquireUrlByKind("update.push"); |
||||||
|
JSONObject pushData = new JSONObject(updatePushInfo); |
||||||
|
|
||||||
|
updateInfo = new DesignerUpdateInfo(currentVersion, latestVersion, lastIgnoredVersion, pushData); |
||||||
|
} |
||||||
|
|
||||||
|
private String getFullLatestVersion() { |
||||||
|
try { |
||||||
|
String res = HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar10.update")); |
||||||
|
return new JSONObject(res).optString("buildNO"); |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
|
||||||
|
private String getVersionByFullNO(String fullNO) { |
||||||
|
if (fullNO.contains(SPLIT_CHAR)) { |
||||||
|
fullNO = fullNO.substring(fullNO.lastIndexOf(SPLIT_CHAR) + 1); |
||||||
|
} |
||||||
|
return fullNO; |
||||||
|
} |
||||||
|
|
||||||
|
private String getPrefixByFullNO(String fullNO) { |
||||||
|
if (fullNO.contains(SPLIT_CHAR)) { |
||||||
|
return fullNO.substring(0, fullNO.lastIndexOf(SPLIT_CHAR)); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* "自动更新推送"选项是否生效 |
||||||
|
*/ |
||||||
|
public boolean isAutoPushUpdateSupported() { |
||||||
|
boolean isLocalEnv = WorkContext.getCurrent().isLocal(); |
||||||
|
boolean isChineseEnv = GeneralContext.isChineseEnv(); |
||||||
|
|
||||||
|
return isAutoPushUpdateSupported(isLocalEnv, isChineseEnv); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isAutoPushUpdateSupported(boolean isLocalEnv, boolean isChineseEnv) { |
||||||
|
// 远程设计和非中文环境,都不生效
|
||||||
|
return isLocalEnv && isChineseEnv; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查更新,如果有合适的更新版本,则弹窗 |
||||||
|
*/ |
||||||
|
public void checkAndPop() { |
||||||
|
new Thread() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (!shouldPopUp()) { |
||||||
|
FineLoggerFactory.getLogger().debug("skip push update"); |
||||||
|
return; |
||||||
|
} |
||||||
|
final DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); |
||||||
|
DesignerPushUpdateDialog.createAndShow(designerFrame, updateInfo); |
||||||
|
} |
||||||
|
}.start(); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean shouldPopUp() { |
||||||
|
if (updateInfo == null) { |
||||||
|
String fullCurrentVersion = GeneralUtils.readFullBuildNO(); |
||||||
|
// todo: 开发测试用
|
||||||
|
if (!fullCurrentVersion.contains(SPLIT_CHAR)) { |
||||||
|
fullCurrentVersion = "stable-2019.01.03.17.01.05.257"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
String fullLatestVersion = getFullLatestVersion(); |
||||||
|
boolean isValidJarVersion = isValidJarVersion(fullCurrentVersion, fullLatestVersion); |
||||||
|
if (!isValidJarVersion) { |
||||||
|
FineLoggerFactory.getLogger().info("Jar version is not valid for push update."); |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
String currentVersion = getVersionByFullNO(fullCurrentVersion); |
||||||
|
String latestVersion = getVersionByFullNO(fullLatestVersion); |
||||||
|
initUpdateInfo(currentVersion, latestVersion); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return isAutoPushUpdateSupported() && updateInfo.hasNewPushVersion(); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isValidJarVersion(String fullCurrentVersion, String fullLatestVersion) { |
||||||
|
// todo: 目前设定的逻辑是 feature/release/stable 都弹,且不区分版本号。后期肯定要变的,注释代码先留着
|
||||||
|
// // 无效的情况:
|
||||||
|
// // 1. 版本号格式有误
|
||||||
|
// // 2. 当前用的是 release 或 feature 的 jar 包
|
||||||
|
// // 3. 代码启动的
|
||||||
|
// String prefix = getPrefixByFullNO(fullLatestVersion);
|
||||||
|
// return StringUtils.isNotEmpty(prefix) && fullCurrentVersion.startsWith(prefix);
|
||||||
|
|
||||||
|
// 无效的情况:
|
||||||
|
// 1. 版本号格式有误(正常情况下都有前缀,只有异常的时候才可能出现)
|
||||||
|
// 2. 代码启动的(fullCurrentVersion 为"不是安装版本")
|
||||||
|
String prefix = getPrefixByFullNO(fullLatestVersion); |
||||||
|
return StringUtils.isNotEmpty(prefix) && fullCurrentVersion.contains(SPLIT_CHAR); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 跳转到更新升级窗口,并自动开始更新 |
||||||
|
*/ |
||||||
|
void doUpdate() { |
||||||
|
new Thread() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
UpdateMainDialog dialog = new UpdateMainDialog(DesignerContext.getDesignerFrame()); |
||||||
|
dialog.setAutoUpdateAfterInit(); |
||||||
|
dialog.showDialog(); |
||||||
|
} |
||||||
|
}.start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 跳过当前的推送版本 |
||||||
|
*/ |
||||||
|
void skipCurrentPushVersion() { |
||||||
|
if (updateInfo == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
config.setLastIgnoredVersion(updateInfo.getPushVersion()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import java.security.InvalidParameterException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/8. |
||||||
|
*/ |
||||||
|
class DesignerUpdateInfo { |
||||||
|
private static final String KEY_VERSION = "version"; |
||||||
|
private static final String KEY_CONTENT = "content"; |
||||||
|
private static final String KEY_BACKGROUND_URL = "background"; |
||||||
|
private static final String KEY_MORE_INFO_URL = "more"; |
||||||
|
|
||||||
|
private final String currentVersion; // 当前版本
|
||||||
|
private final String latestVersion; // 最新版本
|
||||||
|
private final String lastIgnoredVersion; // 最近一次跳过的版本
|
||||||
|
|
||||||
|
private final String pushVersion; // 推送版本
|
||||||
|
private final String pushContent; // 推送更新内容
|
||||||
|
private final String backgroundUrl; // 推送背景图片 url
|
||||||
|
private final String moreInfoUrl; // 更多新特性
|
||||||
|
|
||||||
|
DesignerUpdateInfo(String currentVersion, String latestVersion, String lastIgnoredVersion, JSONObject pushData) { |
||||||
|
this.currentVersion = currentVersion; |
||||||
|
this.latestVersion = latestVersion; |
||||||
|
this.lastIgnoredVersion = lastIgnoredVersion; |
||||||
|
|
||||||
|
this.pushVersion = pushData.optString(KEY_VERSION); |
||||||
|
this.pushContent = pushData.optString(KEY_CONTENT); |
||||||
|
this.backgroundUrl = pushData.optString(KEY_BACKGROUND_URL); |
||||||
|
this.moreInfoUrl = pushData.optString(KEY_MORE_INFO_URL); |
||||||
|
|
||||||
|
// 简单做下参数校验
|
||||||
|
if (hasEmptyField()) { |
||||||
|
throw new InvalidParameterException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean hasEmptyField() { |
||||||
|
// lastIgnoredVersion 可以为空
|
||||||
|
return StringUtils.isEmpty(currentVersion) |
||||||
|
|| StringUtils.isEmpty(latestVersion) |
||||||
|
|| StringUtils.isEmpty(pushVersion) |
||||||
|
|| StringUtils.isEmpty(pushContent) |
||||||
|
|| StringUtils.isEmpty(backgroundUrl) |
||||||
|
|| StringUtils.isEmpty(moreInfoUrl); |
||||||
|
} |
||||||
|
|
||||||
|
String getCurrentVersion() { |
||||||
|
return currentVersion; |
||||||
|
} |
||||||
|
|
||||||
|
String getLatestVersion() { |
||||||
|
return latestVersion; |
||||||
|
} |
||||||
|
|
||||||
|
String getLastIgnoredVersion() { |
||||||
|
return lastIgnoredVersion; |
||||||
|
} |
||||||
|
|
||||||
|
String getPushVersion() { |
||||||
|
return pushVersion; |
||||||
|
} |
||||||
|
|
||||||
|
String getPushContent() { |
||||||
|
return pushContent; |
||||||
|
} |
||||||
|
|
||||||
|
String getBackgroundUrl() { |
||||||
|
return backgroundUrl; |
||||||
|
} |
||||||
|
|
||||||
|
String getMoreInfoUrl() { |
||||||
|
return moreInfoUrl; |
||||||
|
} |
||||||
|
|
||||||
|
boolean hasNewPushVersion() { |
||||||
|
boolean result = ComparatorUtils.compare(pushVersion, currentVersion) > 0 |
||||||
|
&& ComparatorUtils.compare(pushVersion, latestVersion) <= 0; |
||||||
|
if (StringUtils.isNotEmpty(lastIgnoredVersion)) { |
||||||
|
result = result && ComparatorUtils.compare(pushVersion, lastIgnoredVersion) > 0; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.dialog; |
package com.fr.design.update.ui.dialog; |
||||||
|
|
||||||
|
|
||||||
import java.io.File; |
import java.io.File; |
@ -1,13 +1,12 @@ |
|||||||
package com.fr.design.onlineupdate.ui.dialog; |
package com.fr.design.update.ui.dialog; |
||||||
|
|
||||||
import com.fr.design.gui.ibutton.UIButton; |
import com.fr.design.gui.ibutton.UIButton; |
||||||
import com.fr.design.layout.FRGUIPaneFactory; |
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
import com.fr.design.mainframe.DesignerContext; |
import com.fr.design.mainframe.DesignerContext; |
||||||
import com.fr.design.onlineupdate.domain.UpdateConstants; |
import com.fr.design.update.domain.UpdateConstants; |
||||||
import com.fr.design.onlineupdate.factory.DirectoryOperationFactory; |
import com.fr.design.update.factory.DirectoryOperationFactory; |
||||||
import com.fr.design.onlineupdate.ui.widget.ColorfulCellRender; |
import com.fr.design.update.ui.widget.ColorfulCellRender; |
||||||
import com.fr.design.utils.gui.GUICoreUtils; |
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
import com.fr.locale.InterProviderFactory; |
|
||||||
import com.fr.stable.ArrayUtils; |
import com.fr.stable.ArrayUtils; |
||||||
import com.fr.stable.StableUtils; |
import com.fr.stable.StableUtils; |
||||||
|
|
@ -1,14 +1,13 @@ |
|||||||
package com.fr.design.onlineupdate.ui.dialog; |
package com.fr.design.update.ui.dialog; |
||||||
|
|
||||||
import com.fr.base.FRContext; |
import com.fr.base.FRContext; |
||||||
import com.fr.design.RestartHelper; |
import com.fr.design.RestartHelper; |
||||||
import com.fr.design.gui.ibutton.UIButton; |
import com.fr.design.gui.ibutton.UIButton; |
||||||
import com.fr.design.gui.ilable.UILabel; |
import com.fr.design.gui.ilable.UILabel; |
||||||
import com.fr.design.layout.FRGUIPaneFactory; |
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
import com.fr.design.onlineupdate.domain.UpdateConstants; |
import com.fr.design.update.domain.UpdateConstants; |
||||||
import com.fr.design.utils.gui.GUICoreUtils; |
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
import com.fr.general.ComparatorUtils; |
import com.fr.general.ComparatorUtils; |
||||||
import com.fr.locale.InterProviderFactory; |
|
||||||
import com.fr.stable.StableUtils; |
import com.fr.stable.StableUtils; |
||||||
import com.fr.stable.StringUtils; |
import com.fr.stable.StringUtils; |
||||||
import com.fr.stable.project.ProjectConstants; |
import com.fr.stable.project.ProjectConstants; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import javax.swing.JList; |
import javax.swing.JList; |
||||||
import javax.swing.JPanel; |
import javax.swing.JPanel; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import com.fr.design.gui.ilable.UILabel; |
import com.fr.design.gui.ilable.UILabel; |
||||||
import com.fr.general.IOUtils; |
import com.fr.general.IOUtils; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import com.fr.design.gui.ilable.UILabel; |
import com.fr.design.gui.ilable.UILabel; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import javax.swing.JTable; |
import javax.swing.JTable; |
||||||
import javax.swing.table.TableModel; |
import javax.swing.table.TableModel; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import com.fr.general.ComparatorUtils; |
import com.fr.general.ComparatorUtils; |
||||||
import com.fr.stable.StringUtils; |
import com.fr.stable.StringUtils; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import javax.swing.table.AbstractTableModel; |
import javax.swing.table.AbstractTableModel; |
||||||
import java.util.List; |
import java.util.List; |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.onlineupdate.ui.widget; |
package com.fr.design.update.ui.widget; |
||||||
|
|
||||||
import javax.swing.BorderFactory; |
import javax.swing.BorderFactory; |
||||||
import javax.swing.JTable; |
import javax.swing.JTable; |
@ -0,0 +1,12 @@ |
|||||||
|
var arr ="%s".split(".").reverse(); |
||||||
|
var create = function (obj, names) { |
||||||
|
var name = names.pop(); |
||||||
|
if (!name) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!obj[name]) { |
||||||
|
obj[name] = {}; |
||||||
|
} |
||||||
|
create(obj[name], names); |
||||||
|
}; |
||||||
|
create(window, arr); |
@ -0,0 +1,8 @@ |
|||||||
|
var arr = "%s".split(","); |
||||||
|
var header = document.getElementsByTagName("head")[0]; |
||||||
|
arr.forEach(function(el) { |
||||||
|
var script = document.createElement("script") |
||||||
|
script.type = "text/javascript"; |
||||||
|
script.src = "emb:" + el; |
||||||
|
header.appendChild(script); |
||||||
|
}); |
@ -0,0 +1,9 @@ |
|||||||
|
var arr = "%s".split(","); |
||||||
|
var header = document.getElementsByTagName("head")[0]; |
||||||
|
arr.forEach(function(el) { |
||||||
|
var css = document.createElement("link"); |
||||||
|
css.type = "text/css"; |
||||||
|
css.rel = "stylesheet"; |
||||||
|
css.href = "emb:" + el; |
||||||
|
header.appendChild(css); |
||||||
|
}); |
@ -0,0 +1,220 @@ |
|||||||
|
window.addEventListener("load", function (ev) { |
||||||
|
window.BI.i18nText = function(key) {return window.Pool.i18n.i18nText(key);} |
||||||
|
var combo1 = BI.createWidget({ |
||||||
|
type: "bi.vertical", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.text_value_combo", |
||||||
|
text: "选项1", |
||||||
|
width: 300, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项1", |
||||||
|
value: 1 |
||||||
|
}, |
||||||
|
text: "选项1", |
||||||
|
value: 1, |
||||||
|
lgap: 10 |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项2", |
||||||
|
value: 2 |
||||||
|
}, |
||||||
|
lgap: 10, |
||||||
|
text: "选项2", |
||||||
|
value: 2 |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项3", |
||||||
|
value: 3 |
||||||
|
}, |
||||||
|
lgap: 10, |
||||||
|
text: "选项3", |
||||||
|
value: 3 |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
var date = BI.createWidget({ |
||||||
|
type: "bi.left", |
||||||
|
items: [{ |
||||||
|
el: { |
||||||
|
type: "bi.date_time_combo", |
||||||
|
value: { |
||||||
|
year: 2018, |
||||||
|
month: 9, |
||||||
|
day: 28, |
||||||
|
hour: 13, |
||||||
|
minute: 31, |
||||||
|
second: 1 |
||||||
|
} |
||||||
|
} |
||||||
|
}] |
||||||
|
}); |
||||||
|
|
||||||
|
var comboTree = BI.createWidget({ |
||||||
|
type: "bi.vertical", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.tree_value_chooser_combo", |
||||||
|
width: 300, |
||||||
|
itemsCreator: function(op, callback) { |
||||||
|
callback([ |
||||||
|
{ |
||||||
|
id: 1, |
||||||
|
text: "第1项", |
||||||
|
value: "1" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 2, |
||||||
|
text: "第2项", |
||||||
|
value: "2" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 3, |
||||||
|
text: "第3项", |
||||||
|
value: "3", |
||||||
|
open: true |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 11, |
||||||
|
pId: 1, |
||||||
|
text: "子项1", |
||||||
|
value: "11" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 12, |
||||||
|
pId: 1, |
||||||
|
text: "子项2", |
||||||
|
value: "12" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 13, |
||||||
|
pId: 1, |
||||||
|
text: "子项3", |
||||||
|
value: "13" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 31, |
||||||
|
pId: 3, |
||||||
|
text: "子项1", |
||||||
|
value: "31" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 32, |
||||||
|
pId: 3, |
||||||
|
text: "子项2", |
||||||
|
value: "32" |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 33, |
||||||
|
pId: 3, |
||||||
|
text: "子项3", |
||||||
|
value: "33" |
||||||
|
} |
||||||
|
]); |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
var color = BI.createWidget({ |
||||||
|
type: "bi.left", |
||||||
|
items: [{ |
||||||
|
type: "bi.simple_color_chooser", |
||||||
|
width: 24, |
||||||
|
height: 24 |
||||||
|
}, { |
||||||
|
el: { |
||||||
|
type: "bi.color_chooser", |
||||||
|
width: 230, |
||||||
|
height: 24 |
||||||
|
}, |
||||||
|
lgap: 10 |
||||||
|
}] |
||||||
|
}); |
||||||
|
|
||||||
|
var Slider = BI.inherit(BI.Widget, { |
||||||
|
props: { |
||||||
|
width: 300, |
||||||
|
height: 50, |
||||||
|
min: 0, |
||||||
|
max: 100 |
||||||
|
}, |
||||||
|
|
||||||
|
mounted: function() { |
||||||
|
var o = this.options; |
||||||
|
this.singleSliderInterval.setMinAndMax({ |
||||||
|
min: o.min, |
||||||
|
max: o.max |
||||||
|
}); |
||||||
|
|
||||||
|
this.singleSliderInterval.setValue({ |
||||||
|
min: 10, |
||||||
|
max: 80 |
||||||
|
}); |
||||||
|
this.singleSliderInterval.populate(); |
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
var self = this, |
||||||
|
o = this.options; |
||||||
|
return { |
||||||
|
type: "bi.vertical", |
||||||
|
element: this, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.interval_slider", |
||||||
|
digit: 0, |
||||||
|
width: o.width, |
||||||
|
height: o.height, |
||||||
|
ref: function(_ref) { |
||||||
|
self.singleSliderInterval = _ref; |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
} |
||||||
|
}); |
||||||
|
BI.shortcut("demo.slider_interval", Slider); |
||||||
|
var slider = BI.createWidget({ |
||||||
|
type: "demo.slider_interval" |
||||||
|
}); |
||||||
|
|
||||||
|
BI.createWidget({ |
||||||
|
type:"bi.absolute", |
||||||
|
element: "body", |
||||||
|
items: [{ |
||||||
|
el: combo1, |
||||||
|
left: 100, |
||||||
|
top: 100 |
||||||
|
}, { |
||||||
|
el : date, |
||||||
|
left: 100, |
||||||
|
top : 150 |
||||||
|
}, { |
||||||
|
el : comboTree, |
||||||
|
left : 100, |
||||||
|
top : 200 |
||||||
|
}, { |
||||||
|
el : color, |
||||||
|
left : 100, |
||||||
|
top : 250 |
||||||
|
}, { |
||||||
|
el : slider, |
||||||
|
left : 400, |
||||||
|
top : 100 |
||||||
|
}] |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,12 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
##style## |
||||||
|
##script## |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
</body> |
||||||
|
|
||||||
|
</html> |
@ -0,0 +1,22 @@ |
|||||||
|
body { |
||||||
|
padding-left: 30px; |
||||||
|
padding-top: 30px; |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.title { |
||||||
|
font-size: 30px; |
||||||
|
} |
||||||
|
.desc { |
||||||
|
margin-top: 35px; |
||||||
|
} |
||||||
|
.moreInfo { |
||||||
|
margin-top: 15px; |
||||||
|
} |
||||||
|
.buttonGroup { |
||||||
|
margin-top: 35px; |
||||||
|
} |
||||||
|
.button-ignore { |
||||||
|
background-color: white !important; |
||||||
|
border: 1px solid white !important; |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
function i18nText(key) { |
||||||
|
return Pool.data.i18nText(key); |
||||||
|
} |
||||||
|
|
||||||
|
window.addEventListener("load", function (ev) { |
||||||
|
var title = BI.createWidget({ |
||||||
|
type: "bi.vertical", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.label", |
||||||
|
text: i18nText("Fine-Design_Find_New_Version"), |
||||||
|
cls: "title", |
||||||
|
textAlign: "left" |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "bi.label", |
||||||
|
text: Pool.data.getVersion(), |
||||||
|
textAlign: "left" |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
var desc = BI.createWidget({ |
||||||
|
type: "bi.vertical", |
||||||
|
cls: "desc", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.label", |
||||||
|
text: Pool.data.getContent(), |
||||||
|
textAlign: "left" |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
var moreInfo = BI.createWidget({ |
||||||
|
type: "bi.text_button", |
||||||
|
text: i18nText("Fine-Design_Basic_More_Information"), |
||||||
|
cls: "moreInfo", |
||||||
|
textAlign: "left" |
||||||
|
}); |
||||||
|
|
||||||
|
var buttonGroup = BI.createWidget({ |
||||||
|
type: 'bi.left', |
||||||
|
cls: "buttonGroup", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: 'bi.button', |
||||||
|
text: i18nText("Fine-Design_Update_Now"), |
||||||
|
level: 'common', |
||||||
|
height: 30, |
||||||
|
handler: function() { |
||||||
|
Pool.data.updateNow(); |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: 'bi.button', |
||||||
|
text: i18nText("Fine-Design_Remind_Me_Next_Time"), |
||||||
|
level: 'ignore', |
||||||
|
height: 30, |
||||||
|
handler: function() { |
||||||
|
Pool.data.remindNextTime(); |
||||||
|
} |
||||||
|
}, |
||||||
|
lgap: 10 |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: 'bi.button', |
||||||
|
text: i18nText("Fine-Design_Skip_This_Version"), |
||||||
|
level: 'ignore', |
||||||
|
height: 30, |
||||||
|
handler: function() { |
||||||
|
Pool.data.skipThisVersion(); |
||||||
|
} |
||||||
|
}, |
||||||
|
lgap: 10 |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
var container = BI.createWidget({ |
||||||
|
type:"bi.vertical", |
||||||
|
element: "body", |
||||||
|
cls: "container", |
||||||
|
items: [ |
||||||
|
title, |
||||||
|
desc, |
||||||
|
moreInfo, |
||||||
|
buttonGroup |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
container.element.css("background", "url(" + Pool.data.getBackgroundUrl() + ")"); |
||||||
|
}); |
@ -0,0 +1,30 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
|
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.WindowConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-07 |
||||||
|
*/ |
||||||
|
public class FineUIDemo { |
||||||
|
|
||||||
|
public static void main(String... args) { |
||||||
|
final JFrame frame = new JFrame(); |
||||||
|
frame.setSize(1200, 800); |
||||||
|
JPanel contentPane = (JPanel) frame.getContentPane(); |
||||||
|
// 是否需要开启调试窗口
|
||||||
|
DesignerEnvManager.getEnvManager().setOpenDebug(true); |
||||||
|
|
||||||
|
final ModernUIPane<ModernUIPaneTest.Model> pane = new ModernUIPane.Builder<ModernUIPaneTest.Model>() |
||||||
|
.withComponent(StartComponent.KEY).build(); |
||||||
|
contentPane.add(pane, BorderLayout.CENTER); |
||||||
|
frame.setVisible(true); |
||||||
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
|
||||||
|
import javax.swing.JButton; |
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.WindowConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-05 |
||||||
|
*/ |
||||||
|
public class ModernUIPaneTest { |
||||||
|
|
||||||
|
public static void main(String... args) { |
||||||
|
final JFrame frame = new JFrame(); |
||||||
|
frame.setSize(1200, 800); |
||||||
|
JPanel contentPane = (JPanel) frame.getContentPane(); |
||||||
|
// 是否需要开启调试窗口
|
||||||
|
DesignerEnvManager.getEnvManager().setOpenDebug(true); |
||||||
|
final ModernUIPane<Model> pane = new ModernUIPane.Builder<Model>() |
||||||
|
.withEMB("/com/fr/design/ui/demo.html").namespace("Pool").build(); |
||||||
|
contentPane.add(pane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
Model model = new Model(); |
||||||
|
model.setAge(20); |
||||||
|
model.setName("Pick"); |
||||||
|
pane.populate(model); |
||||||
|
|
||||||
|
JPanel panel = new JPanel(new FlowLayout()); |
||||||
|
contentPane.add(panel, BorderLayout.SOUTH); |
||||||
|
JButton button = new JButton("点击我可以看到Swing的弹框,输出填写的信息"); |
||||||
|
panel.add(button); |
||||||
|
button.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
Model returnValue = pane.update(); |
||||||
|
if (returnValue != null) { |
||||||
|
JOptionPane.showMessageDialog(frame, String.format("姓名为:%s,年龄为:%d", returnValue.getName(), returnValue.getAge())); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
frame.setVisible(true); |
||||||
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); |
||||||
|
} |
||||||
|
|
||||||
|
public static class Model { |
||||||
|
private String name; |
||||||
|
private int age; |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public int getAge() { |
||||||
|
return age; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAge(int age) { |
||||||
|
this.age = age; |
||||||
|
} |
||||||
|
|
||||||
|
public void print(String message) { |
||||||
|
System.out.println(message); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.design.ui; |
||||||
|
|
||||||
|
import com.fr.web.struct.AssembleComponent; |
||||||
|
import com.fr.web.struct.Atom; |
||||||
|
import com.fr.web.struct.browser.RequestClient; |
||||||
|
import com.fr.web.struct.category.ScriptPath; |
||||||
|
import com.fr.web.struct.impl.FineUI; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-03-08 |
||||||
|
*/ |
||||||
|
public class StartComponent extends AssembleComponent { |
||||||
|
|
||||||
|
public static final StartComponent KEY = new StartComponent(); |
||||||
|
|
||||||
|
private StartComponent() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ScriptPath script(RequestClient req) { |
||||||
|
return ScriptPath.build("/com/fr/design/ui/script/start.js"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Atom[] refer() { |
||||||
|
return new Atom[] {FineUI.KEY}; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
import com.fr.third.javax.xml.stream.XMLStreamException; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.StringReader; |
||||||
|
import java.io.StringWriter; |
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.junit.Assert.assertSame; |
||||||
|
import static org.junit.Assert.assertTrue; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/8. |
||||||
|
*/ |
||||||
|
public class DesignerPushUpdateConfigManagerTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSingleton() { |
||||||
|
DesignerPushUpdateConfigManager m1 = DesignerPushUpdateConfigManager.getInstance(); |
||||||
|
DesignerPushUpdateConfigManager m2 = DesignerPushUpdateConfigManager.getInstance(); |
||||||
|
assertSame(m1, m2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDefaultValue() throws XMLStreamException { |
||||||
|
DesignerPushUpdateConfigManager configManager = DesignerPushUpdateConfigManager.getInstance(); |
||||||
|
XMLableReader xmlReader = XMLableReader.createXMLableReader(new StringReader("<xml></xml>")); |
||||||
|
xmlReader.readXMLObject(configManager); |
||||||
|
|
||||||
|
assertEquals(StringUtils.EMPTY, configManager.getLastIgnoredVersion()); |
||||||
|
assertTrue(configManager.isAutoPushUpdateEnabled()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testReadAndWrite() throws XMLStreamException { |
||||||
|
final String initLastIngnoredVersion = "1.1.2"; |
||||||
|
final boolean initAutoPushEnabled = false; |
||||||
|
|
||||||
|
DesignerPushUpdateConfigManager configManager = DesignerPushUpdateConfigManager.getInstance(); |
||||||
|
|
||||||
|
configManager.setLastIgnoredVersion(initLastIngnoredVersion); |
||||||
|
configManager.setAutoPushUpdateEnabled(initAutoPushEnabled); |
||||||
|
|
||||||
|
// 写入 xml
|
||||||
|
StringWriter sw = new StringWriter(); |
||||||
|
XMLPrintWriter writer = XMLPrintWriter.create(new PrintWriter(sw)); |
||||||
|
configManager.writeXML(writer); |
||||||
|
writer.flush(); |
||||||
|
writer.close(); |
||||||
|
|
||||||
|
String xml_str = sw.getBuffer().toString(); |
||||||
|
|
||||||
|
// 临时修改配置
|
||||||
|
configManager.setAutoPushUpdateEnabled(true); |
||||||
|
configManager.setLastIgnoredVersion("0.20.1"); |
||||||
|
|
||||||
|
// 从 xml 中读取
|
||||||
|
StringReader sr = new StringReader(xml_str); |
||||||
|
XMLableReader xmlReader = XMLableReader.createXMLableReader(sr); |
||||||
|
xmlReader.readXMLObject(configManager); |
||||||
|
|
||||||
|
// 验证:与写入时的配置一致
|
||||||
|
assertEquals(initLastIngnoredVersion, configManager.getLastIgnoredVersion()); |
||||||
|
assertEquals(initAutoPushEnabled, configManager.isAutoPushUpdateEnabled()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/10. |
||||||
|
*/ |
||||||
|
public class DesignerPushUpdateDialogTest { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
DesignerEnvManager.getEnvManager().setOpenDebug(true); |
||||||
|
|
||||||
|
JSONObject jo = JSONObject.create(); |
||||||
|
jo.put("version", "2019.03.06.04.02.43.6"); |
||||||
|
jo.put("content", "test content"); |
||||||
|
jo.put("more", "http://baidu.com"); |
||||||
|
jo.put("background", "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1555043827901&di=fc266992abef5a7e13b4e0cb98975a75&imgtype=0&src=http%3A%2F%2Fi5.3conline.com%2Fimages%2Fpiclib%2F201203%2F20%2Fbatch%2F1%2F130280%2F1332249463721rez0li5fg0_medium.jpg"); |
||||||
|
DesignerUpdateInfo mockUpdateInfo = new DesignerUpdateInfo("111.22.11", "2211.231.1", "11.23.1", jo); |
||||||
|
|
||||||
|
DesignerPushUpdateDialog.createAndShow(null, mockUpdateInfo); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.design.event.DesignerOpenedListener; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrame; |
||||||
|
import com.fr.invoke.Reflect; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import org.easymock.EasyMock; |
||||||
|
import org.junit.BeforeClass; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.powermock.api.easymock.PowerMock; |
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest; |
||||||
|
import org.powermock.modules.junit4.PowerMockRunner; |
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.junit.Assert.assertSame; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/8. |
||||||
|
*/ |
||||||
|
@RunWith(value = PowerMockRunner.class) |
||||||
|
@PrepareForTest(DesignerContext.class) |
||||||
|
public class DesignerPushUpdateManagerTest { |
||||||
|
|
||||||
|
@BeforeClass |
||||||
|
public static void setUp() { |
||||||
|
DesignerFrame mockFrame = EasyMock.mock(DesignerFrame.class); |
||||||
|
mockFrame.addDesignerOpenedListener(EasyMock.anyObject(DesignerOpenedListener.class)); |
||||||
|
EasyMock.replay(mockFrame); |
||||||
|
|
||||||
|
PowerMock.mockStatic(DesignerContext.class); |
||||||
|
EasyMock.expect(DesignerContext.getDesignerFrame()).andReturn(mockFrame).anyTimes(); |
||||||
|
PowerMock.replayAll(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSingleton() { |
||||||
|
DesignerPushUpdateManager m1 = DesignerPushUpdateManager.getInstance(); |
||||||
|
DesignerPushUpdateManager m2 = DesignerPushUpdateManager.getInstance(); |
||||||
|
assertSame(m1, m2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIsAutoPushUpdateSupported() { |
||||||
|
// 中文环境 + 本地设计 -> true
|
||||||
|
DesignerPushUpdateManager pushUpdateManager = DesignerPushUpdateManager.getInstance(); |
||||||
|
assertEquals(true, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", true, true).get()); |
||||||
|
|
||||||
|
// 非中文环境 || 远程设计 -> false
|
||||||
|
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", false, true).get()); |
||||||
|
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", true, false).get()); |
||||||
|
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", false, false).get()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSkipCurrentPushVersion() { |
||||||
|
DesignerPushUpdateManager pushUpdateManager = DesignerPushUpdateManager.getInstance(); |
||||||
|
|
||||||
|
// 1. updateInfo 为 null 的情况
|
||||||
|
pushUpdateManager.skipCurrentPushVersion(); |
||||||
|
assertEquals(StringUtils.EMPTY, DesignerPushUpdateConfigManager.getInstance().getLastIgnoredVersion()); |
||||||
|
|
||||||
|
|
||||||
|
// 2. updateInfo 有值的情况
|
||||||
|
final String PUSH_VERSION = "stable-2019.02.03.12.44.22"; |
||||||
|
DesignerUpdateInfo mockInfo = EasyMock.mock(DesignerUpdateInfo.class); |
||||||
|
EasyMock.expect(mockInfo.getPushVersion()).andReturn(PUSH_VERSION).anyTimes(); |
||||||
|
Reflect.on(pushUpdateManager).set("updateInfo", mockInfo); |
||||||
|
EasyMock.replay(mockInfo); |
||||||
|
|
||||||
|
pushUpdateManager.skipCurrentPushVersion(); |
||||||
|
assertEquals(PUSH_VERSION, DesignerPushUpdateConfigManager.getInstance().getLastIgnoredVersion()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package com.fr.design.update.push; |
||||||
|
|
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import java.security.InvalidParameterException; |
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.junit.Assert.assertFalse; |
||||||
|
import static org.junit.Assert.assertTrue; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by plough on 2019/4/9. |
||||||
|
*/ |
||||||
|
public class DesignerUpdateInfoTest { |
||||||
|
private static final String CURRENT_VERSION = "2018.09.03.xx"; |
||||||
|
private static final String LATEST_VERSION = "2019.04.03.yy"; |
||||||
|
private static final String LAST_IGNORED_VERSION = "2019.02.03.yy"; |
||||||
|
private static final String PUSH_VERSION = "2019.01.03.21.11"; |
||||||
|
private static final String PUSH_CONTENT = "the update desc content"; |
||||||
|
private static final String PUSH_BACKGROUND = "http://image.fr.com/123.jpg"; |
||||||
|
private static final String PUSH_MORE = "http://help.finereport.com/xxx"; |
||||||
|
private DesignerUpdateInfo updateInfo; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
JSONObject pushData = JSONObject.create(); |
||||||
|
|
||||||
|
pushData.put("version", PUSH_VERSION); |
||||||
|
pushData.put("content", PUSH_CONTENT); |
||||||
|
pushData.put("background", PUSH_BACKGROUND); |
||||||
|
pushData.put("more", PUSH_MORE); |
||||||
|
|
||||||
|
updateInfo = new DesignerUpdateInfo(CURRENT_VERSION, LATEST_VERSION, LAST_IGNORED_VERSION, pushData); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetters() { |
||||||
|
assertEquals(CURRENT_VERSION, updateInfo.getCurrentVersion()); |
||||||
|
assertEquals(LATEST_VERSION, updateInfo.getLatestVersion()); |
||||||
|
assertEquals(LAST_IGNORED_VERSION, updateInfo.getLastIgnoredVersion()); |
||||||
|
assertEquals(PUSH_VERSION, updateInfo.getPushVersion()); |
||||||
|
assertEquals(PUSH_CONTENT, updateInfo.getPushContent()); |
||||||
|
assertEquals(PUSH_BACKGROUND, updateInfo.getBackgroundUrl()); |
||||||
|
assertEquals(PUSH_MORE, updateInfo.getMoreInfoUrl()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testHasNewPushVersion() { |
||||||
|
// (1)最近被跳过的维护版本号 X0;
|
||||||
|
// (2)本地版本号 Y;
|
||||||
|
// (3)最新的推送版本号 X;
|
||||||
|
// (4)最新的版本号 Z
|
||||||
|
// 必须满足:Y < X <= Z && X > X0,才返回 true
|
||||||
|
|
||||||
|
// 1 true
|
||||||
|
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2018.05.03.xx")); |
||||||
|
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", null)); |
||||||
|
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", StringUtils.EMPTY)); |
||||||
|
|
||||||
|
// 2 false
|
||||||
|
// 2.1 X <= Y && X > X0
|
||||||
|
assertFalse(hasNewVersion("2019.01.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx")); |
||||||
|
assertFalse(hasNewVersion("2019.03.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx")); |
||||||
|
|
||||||
|
// 2.2 X > Z && X > X0
|
||||||
|
assertFalse(hasNewVersion("2020.01.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx")); |
||||||
|
|
||||||
|
// 2.3 Y < X <= Z && X <= X0
|
||||||
|
assertFalse(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2019.02.03.xx")); |
||||||
|
assertFalse(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2019.01.03.xx")); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private boolean hasNewVersion(String X, String Y, String Z, String X0) { |
||||||
|
JSONObject pushData = JSONObject.create(); |
||||||
|
pushData.put("version", X); |
||||||
|
pushData.put("content", PUSH_CONTENT); |
||||||
|
pushData.put("background", PUSH_BACKGROUND); |
||||||
|
pushData.put("more", PUSH_MORE); |
||||||
|
DesignerUpdateInfo updateInfo = new DesignerUpdateInfo(Y, Z, X0, pushData); |
||||||
|
return updateInfo.hasNewPushVersion(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParameterValidation() { |
||||||
|
try { |
||||||
|
DesignerUpdateInfo updateInfo = new DesignerUpdateInfo(null, null, null, new JSONObject()); |
||||||
|
Assert.fail("should not reach here!"); |
||||||
|
} catch (InvalidParameterException e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
<script type="text/javascript"> |
||||||
|
Pool.update = function () { |
||||||
|
Pool.data.setAge(parseInt(document.getElementById("age").value)); |
||||||
|
Pool.data.setName(document.getElementById("name").value); |
||||||
|
return Pool.data; |
||||||
|
}; |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div>测试页面,请点击最下面的按钮</div> |
||||||
|
<input id="name" type="text" placeholder="请输入名字"/> |
||||||
|
<input id="age" type="number" placeholder="请输入年龄"/> |
||||||
|
<img src="emb:/com/fr/design/images/splash_10.gif"> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,11 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
<link rel="stylesheet" href="emb:/com/fr/web/ui/fineui.min.css"/> |
||||||
|
<script src="emb:/com/fr/web/ui/fineui.min.js"/> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,57 @@ |
|||||||
|
window.addEventListener("load", function (ev) { |
||||||
|
var combo1 = BI.createWidget({ |
||||||
|
type: "bi.vertical", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "bi.text_value_combo", |
||||||
|
text: "选项1", |
||||||
|
width: 300, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项1", |
||||||
|
value: 1 |
||||||
|
}, |
||||||
|
text: "选项1", |
||||||
|
value: 1, |
||||||
|
lgap: 10 |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项2", |
||||||
|
value: 2 |
||||||
|
}, |
||||||
|
lgap: 10, |
||||||
|
text: "选项2", |
||||||
|
value: 2 |
||||||
|
}, |
||||||
|
{ |
||||||
|
el: { |
||||||
|
type: "bi.single_select_radio_item", |
||||||
|
width: 290, |
||||||
|
text: "选项3", |
||||||
|
value: 3 |
||||||
|
}, |
||||||
|
lgap: 10, |
||||||
|
text: "选项3", |
||||||
|
value: 3 |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
BI.createWidget({ |
||||||
|
type:"bi.absolute", |
||||||
|
element: "body", |
||||||
|
items: [{ |
||||||
|
el: combo1, |
||||||
|
left: 100, |
||||||
|
top: 100 |
||||||
|
}] |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue