diff --git a/README.md b/README.md
index bc5580e..d962401 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-8491
-JSD-8491 平台功能扩展 开源任务材料
\ No newline at end of file
+JSD-8491 平台功能扩展 开源任务材料\
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/lib/finekit-10.0.jar b/lib/finekit-10.0.jar
new file mode 100644
index 0000000..611c8f5
Binary files /dev/null and b/lib/finekit-10.0.jar differ
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..0b9a3be
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,21 @@
+
+ com.fr.plugin.third.party.jsdiejb
+
+ yes
+ 0.8
+ 10.0
+ 2019-01-01
+ fr.open
+
+
+
+ *
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/components/FileDef.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/components/FileDef.java
new file mode 100644
index 0000000..324101f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/components/FileDef.java
@@ -0,0 +1,54 @@
+package com.fr.plugin.third.party.jsdiejb.components;
+
+import com.fanruan.api.log.LogKit;
+import com.fr.plugin.context.PluginContexts;
+import com.fr.web.struct.Component;
+import com.fr.web.struct.Filter;
+import com.fr.web.struct.browser.RequestClient;
+import com.fr.web.struct.category.ScriptPath;
+import com.fr.web.struct.category.StylePath;
+
+
+public class FileDef extends Component {
+
+ public static final FileDef KEY = new FileDef();
+ private FileDef(){}
+
+ /**
+ * 返回需要引入的JS脚本路径
+ * @param client 请求客户端描述
+ * @return JS脚本路径
+ */
+ @Override
+ public ScriptPath script(RequestClient client) {
+ return ScriptPath.build("com/fr/plugin/third/party/jsdiejb/web/main.js");
+ }
+
+ /**
+ * 返回需要引入的CSS样式路径
+ * @param client 请求客户端描述
+ * @return CSS样式路径
+ */
+ @Override
+ public StylePath style(RequestClient client) {
+// //如果不需要就直接返回 StylePath.EMPTY;
+ return StylePath.build("com/fr/plugin/third/party/jsdiejb/web/main.css");
+ //return StylePath.EMPTY;
+ }
+
+
+ @Override
+ public Filter filter() {
+ return new Filter(){
+ public boolean accept() {
+ //添加认证
+ if (!PluginContexts.currentContext().isAvailable()) {
+ LogKit.error("The jsdiejb plugin trial is over, please purchase a license");
+ return false;
+ }
+ //任何情况下我们都在平台组件加载时加载我们的组件
+ return true;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/components/WebResourceProvider.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/components/WebResourceProvider.java
new file mode 100644
index 0000000..7076946
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/components/WebResourceProvider.java
@@ -0,0 +1,18 @@
+package com.fr.plugin.third.party.jsdiejb.components;
+
+import com.fr.decision.fun.impl.AbstractWebResourceProvider;
+import com.fr.decision.web.MainComponent;
+import com.fr.stable.fun.Authorize;
+import com.fr.web.struct.Atom;
+@Authorize(callSignKey = "com.fr.plugin.third.party.jsdiejb")
+public class WebResourceProvider extends AbstractWebResourceProvider {
+ @Override
+ public Atom attach() {
+ return MainComponent.KEY;
+ }
+
+ @Override
+ public Atom client() {
+ return FileDef.KEY;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/config/CustomDataConfig.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/config/CustomDataConfig.java
new file mode 100644
index 0000000..f79f668
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/config/CustomDataConfig.java
@@ -0,0 +1,117 @@
+package com.fr.plugin.third.party.jsdiejb.config;
+
+
+import com.fanruan.api.util.StringKit;
+import com.fr.config.*;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+
+/**
+ * 配置数据保存
+ */
+@Visualization(category = "悬浮球配置")
+public class CustomDataConfig extends DefaultConfiguration {
+ public String getNameSpace() {
+ return this.getClass().getName();
+ }
+
+ private static volatile CustomDataConfig config = null;
+
+ public static CustomDataConfig getInstance() {
+ if (config == null) {
+ config = ConfigContext.getConfigInstance(CustomDataConfig.class);
+ }
+ return config;
+ }
+
+ @Identifier(value = "reportTitle", name = "报表标题", description = "", status = Status.SHOW)
+ private Conf reportTitle = Holders.simple(StringKit.EMPTY);
+
+ @Identifier(value = "reportUrl", name = "报表链接", description = "", status = Status.SHOW)
+ private Conf reportUrl = Holders.simple(StringKit.EMPTY);
+
+
+ @Identifier(value = "reportWidth", name = "报表显示宽度", description = "", status = Status.SHOW)
+ private Conf reportWidth = Holders.simple(800);
+
+
+ @Identifier(value = "reportHeight", name = "报表显示高度", description = "", status = Status.SHOW)
+ private Conf reportHeight = Holders.simple(600);
+
+
+ @Identifier(value = "iconWidth", name = "图标宽度", description = "", status = Status.SHOW)
+ private Conf iconWidth = Holders.simple(40);
+
+ @Identifier(value = "iconName", name = "图标名称", description = "", status = Status.HIDE)
+ private Conf iconName = Holders.simple(StringKit.EMPTY);
+
+ public String getIconName() {
+ return iconName.get();
+ }
+
+ public void setIconName(String iconName) {
+ this.iconName.set(iconName);
+ }
+
+ public Integer getIconWidth() {
+ return iconWidth.get();
+ }
+
+ public void setIconWidth(Integer iconWidth) {
+ this.iconWidth.set(iconWidth);
+ }
+
+ public String getReportTitle() {
+ return trimStr(reportTitle.get());
+ }
+
+ public void setReportTitle(String reportTitle) {
+ this.reportTitle.set(reportTitle);
+ }
+
+ public String getReportUrl() {
+ return trimStr(reportUrl.get());
+ }
+
+ public void setReportUrl(String reportUrl) {
+ this.reportUrl.set(reportUrl);
+ }
+
+ public Integer getReportWidth() {
+ return reportWidth.get();
+ }
+
+ public void setReportWidth(Integer reportWidth) {
+ this.reportWidth.set(reportWidth);
+ }
+
+ public Integer getReportHeight() {
+ return reportHeight.get();
+ }
+
+ public void setReportHeight(Integer reportHeight) {
+ this.reportHeight.set(reportHeight);
+ }
+
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ CustomDataConfig cloned = (CustomDataConfig) super.clone();
+ cloned.reportTitle = (Conf) reportTitle.clone();
+ cloned.reportUrl = (Conf) reportUrl.clone();
+ cloned.reportWidth = (Conf) reportWidth.clone();
+ cloned.reportHeight = (Conf) reportHeight.clone();
+ cloned.iconWidth = (Conf) iconWidth.clone();
+ cloned.iconName = (Conf) iconName.clone();
+ return cloned;
+ }
+
+ private String trimStr(String value) {
+ if (StringKit.isEmpty(value)) {
+ return "";
+ }
+ String tempValue = value.trim();
+ return tempValue;
+ }
+
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/config/DataConfigInitializeMonitor.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/config/DataConfigInitializeMonitor.java
new file mode 100644
index 0000000..154f3fb
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/config/DataConfigInitializeMonitor.java
@@ -0,0 +1,23 @@
+package com.fr.plugin.third.party.jsdiejb.config;
+
+import com.fr.intelli.record.Focus;
+import com.fr.intelli.record.Original;
+import com.fr.plugin.context.PluginContext;
+import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor;
+import com.fr.plugin.third.party.jsdiejb.config.CustomDataConfig;
+import com.fr.record.analyzer.EnableMetrics;
+
+
+@EnableMetrics
+public class DataConfigInitializeMonitor extends AbstractPluginLifecycleMonitor {
+ @Override
+ @Focus(id = "com.fr.plugin.third.party.jsdiejb", text = "plugin-jsd-iejb", source = Original.PLUGIN)
+ public void afterRun(PluginContext pluginContext) {
+ CustomDataConfig.getInstance();
+ }
+
+ @Override
+ public void beforeStop(PluginContext pluginContext) {
+
+ }
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomConfigHttpHandler.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomConfigHttpHandler.java
new file mode 100644
index 0000000..ea20ae3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomConfigHttpHandler.java
@@ -0,0 +1,53 @@
+package com.fr.plugin.third.party.jsdiejb.http;
+
+import com.fr.decision.fun.impl.BaseHttpHandler;
+import com.fr.decision.webservice.bean.user.UserDataSetBean;
+import com.fr.decision.webservice.v10.config.ConfigService;
+import com.fr.decision.webservice.v10.user.SyncService;
+import com.fr.intelli.record.Focus;
+import com.fr.intelli.record.Original;
+import com.fr.json.JSONObject;
+import com.fr.plugin.third.party.jsdiejb.config.CustomDataConfig;
+import com.fr.record.analyzer.EnableMetrics;
+import com.fr.third.springframework.web.bind.annotation.RequestMethod;
+import com.fr.web.controller.common.ResourcesRequestService;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ */
+public class CustomConfigHttpHandler extends BaseHttpHandler {
+
+ @Override
+ public RequestMethod getMethod() {
+ return RequestMethod.GET;
+ }
+
+ @Override
+ public String getPath() {
+ return "/jsdiejb/config";
+ }
+
+ @Override
+ public boolean isPublic() {
+ return true;
+ }
+
+ @Override
+ public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception {
+ res.setContentType("application/json; charset=utf-8");
+ JSONObject configJson = new JSONObject();
+ configJson.put("reportTitle", CustomDataConfig.getInstance().getReportTitle());
+ configJson.put("reportUrl", CustomDataConfig.getInstance().getReportUrl());
+ configJson.put("reportWidth", CustomDataConfig.getInstance().getReportWidth());
+ configJson.put("reportHeight", CustomDataConfig.getInstance().getReportHeight());
+ configJson.put("iconWidth", CustomDataConfig.getInstance().getIconWidth());
+ //configJson.put("iconName", CustomDataConfig.getInstance().getIconName());
+ WebUtils.printAsJSON(res, configJson);
+ }
+
+
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomHttpHandlerProvider.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomHttpHandlerProvider.java
new file mode 100644
index 0000000..f895452
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomHttpHandlerProvider.java
@@ -0,0 +1,14 @@
+package com.fr.plugin.third.party.jsdiejb.http;
+
+import com.fr.decision.fun.impl.AbstractHttpHandlerProvider;
+import com.fr.decision.fun.impl.BaseHttpHandler;
+
+public class CustomHttpHandlerProvider extends AbstractHttpHandlerProvider {
+ @Override
+ public BaseHttpHandler[] registerHandlers() {
+ return new BaseHttpHandler[]{
+ new CustomConfigHttpHandler(),
+ new ResourcesHttpHandler()
+ };
+ }
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomURLAliasProvider.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomURLAliasProvider.java
new file mode 100644
index 0000000..8a9e3f6
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/CustomURLAliasProvider.java
@@ -0,0 +1,15 @@
+package com.fr.plugin.third.party.jsdiejb.http;
+
+import com.fr.decision.fun.impl.AbstractURLAliasProvider;
+import com.fr.decision.webservice.url.alias.URLAlias;
+import com.fr.decision.webservice.url.alias.URLAliasFactory;
+
+public class CustomURLAliasProvider extends AbstractURLAliasProvider {
+ @Override
+ public URLAlias[] registerAlias() {
+ return new URLAlias[]{
+ URLAliasFactory.createPluginAlias("/jsdiejb/config", "/jsdiejb/config", true),
+ URLAliasFactory.createPluginAlias("/jsdiejb/resources", "/jsdiejb/resources", true)
+ };
+ }
+}
diff --git a/src/main/java/com/fr/plugin/third/party/jsdiejb/http/ResourcesHttpHandler.java b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/ResourcesHttpHandler.java
new file mode 100644
index 0000000..42515e0
--- /dev/null
+++ b/src/main/java/com/fr/plugin/third/party/jsdiejb/http/ResourcesHttpHandler.java
@@ -0,0 +1,101 @@
+package com.fr.plugin.third.party.jsdiejb.http;
+
+import com.fr.base.TemplateUtils;
+import com.fr.base.Utils;
+import com.fr.decision.fun.impl.BaseHttpHandler;
+import com.fr.decision.webservice.utils.WebServiceUtils;
+import com.fr.general.IOUtils;
+import com.fr.json.JSONObject;
+import com.fr.plugin.third.party.jsdiejb.config.CustomDataConfig;
+import com.fr.stable.StableUtils;
+import com.fr.third.springframework.web.bind.annotation.RequestMethod;
+import com.fr.web.ResourceType;
+import com.fr.web.controller.common.ResourceUtils;
+import com.fr.web.core.gzip.GZIPResponseWrapper;
+import com.fr.web.utils.WebUtils;
+import com.fr.workspace.WorkContext;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.util.Collections;
+
+/**
+ *
+ */
+public class ResourcesHttpHandler extends BaseHttpHandler {
+
+ @Override
+ public RequestMethod getMethod() {
+ return RequestMethod.GET;
+ }
+
+ @Override
+ public String getPath() {
+ return "/jsdiejb/resources";
+ }
+
+ @Override
+ public boolean isPublic() {
+ return true;
+ }
+
+ @Override
+ public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception {
+ String path = WebUtils.getHTTPRequestParameter(req, "path");
+ if (path != null) {
+ path = path.endsWith("?") ? path.substring(0, path.length() - 1) : path;
+ }
+
+ ResourceType var4 = WebUtils.setResourceContentType(path, (HttpServletResponse) res);
+ if (var4 == ResourceType.FILE || var4 == ResourceType.OTHER) {
+ GZIPResponseWrapper var5 = WebServiceUtils.getGZIPResponseWrapper(req, (HttpServletResponse) res);
+ if (var5 != null) {
+ res = var5;
+ }
+ InputStream var6 = readResourceByWeb(path);
+ if (var6 != null) {
+ switch (var4) {
+ case FILE:
+ PrintWriter var7 = WebUtils.createPrintWriter((HttpServletResponse) res);
+ TemplateUtils.dealWithTemplate(var6, "UTF-8", var7, Collections.EMPTY_MAP);
+ var7.flush();
+ var7.close();
+ break;
+ case OTHER:
+ ServletOutputStream var8 = ((HttpServletResponse) res).getOutputStream();
+ Utils.copyBinaryTo(var6, var8);
+ var8.flush();
+ var8.close();
+ }
+
+ var6.close();
+ if (var5 != null) {
+ var5.finishResponse();
+ }
+
+ }
+ }
+ }
+
+ public static InputStream readResourceByWeb(String path) throws Exception {
+ if ((path == null) || (path.length() <= 0)) {
+ return null;
+ }
+ File var1 = new File(path);
+ if (var1.exists()) {
+ String var4 = WorkContext.getCurrent().getPath();
+ String var3 = (new File(var4)).getParent();
+ return var1.getAbsolutePath().startsWith(var3) ? new FileInputStream(var1) : null;
+ }
+ path = path.startsWith("/") ? path : "/" + path;
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(WorkContext.getWorkResource().readFully(StableUtils.pathJoin(new String[]{"resources", path})));
+ if (byteArrayInputStream.available() >= 1) {
+ return byteArrayInputStream;
+ }
+
+ InputStream inputStream = IOUtils.readResource(path);
+ return inputStream;
+ }
+}
diff --git a/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.css b/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.css
new file mode 100644
index 0000000..441b676
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.css
@@ -0,0 +1,29 @@
+.jsdiejb-ball {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ background-repeat: no-repeat;
+ background-size: cover;
+ z-index: 100001;
+ top: 80px;
+ right: 0px;
+}
+
+.jsdiejb-report-content {
+ position: absolute;
+ width: 800px;
+ height: 600px;
+ z-index: 100000;
+ top: 25%;
+ left: 25%;
+ display: none;
+ background-color: white;
+ border-width: 0px;
+ overflow: hidden;
+}
+
+.jsdiejb-report-iframe {
+ width: 100%;
+ height: 100%;
+ border-width: 0px;
+}
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.js b/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.js
new file mode 100644
index 0000000..85fb38e
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/third/party/jsdiejb/web/main.js
@@ -0,0 +1,173 @@
+Dec.Utils = Dec.Utils || {};
+Dec.Utils.jsdiejb = Dec.Utils.jsdiejb || {};
+
+Dec.Utils.jsdiejb.config = {};
+Dec.Utils.jsdiejb.openOption = 0;
+Dec.Utils.jsdiejb.openOptionSrc = 0;
+
+BI.extend(Dec.Utils.jsdiejb, {
+ getConfig: function (e) {
+ Dec.reqGet("/url/jsdiejb/config", "", e)
+ },
+ dragendBall: function (e) {
+ e.preventDefault ? e.preventDefault() : (e.returnValue = false);
+ //debugger;
+ //console.log(e);
+ var x = e.clientX;
+ var y = e.clientY;
+
+ var maxLeft = $('body').width() - 100;
+ var maxTop = $('body').height() - 100;
+ if (maxLeft <= 0) {
+ maxLeft = 0;
+ }
+ if (maxTop <= 0) {
+ maxTop = 0;
+ }
+ var leftValue = x;
+ var topValue = y;
+ if (leftValue <= 0) {
+ leftValue = 0;
+ }
+ if (topValue <= 0) {
+ topValue = 0;
+ }
+
+ if (leftValue > maxLeft) {
+ leftValue = maxLeft;
+ }
+
+ if (topValue > maxTop) {
+ topValue = maxTop;
+ }
+
+ $('.jsdiejb-ball').css("left", leftValue + "px");
+ $('.jsdiejb-ball').css("top", topValue + "px");
+ },
+ resizeBall: function () {
+ //debugger;
+ var maxLeft = $('body').width() - 100;
+ var maxTop = $('body').height() - 100;
+ if (maxLeft <= 0) {
+ maxLeft = 0;
+ }
+ if (maxTop <= 0) {
+ maxTop = 0;
+ }
+ var leftValue = $('.jsdiejb-ball').position().left;
+ var topValue = $('.jsdiejb-ball').position().top;
+ if (leftValue <= 0) {
+ leftValue = 0;
+ }
+ if (topValue <= 0) {
+ topValue = 0;
+ }
+
+ if (leftValue > maxLeft) {
+ leftValue = maxLeft;
+ }
+
+ if (topValue > maxTop) {
+ topValue = maxTop;
+ }
+
+ $('.jsdiejb-ball').css("left", leftValue + "px");
+ $('.jsdiejb-ball').css("top", topValue + "px");
+ },
+ openUrl: function (e) {
+ //if (e.button != 2) {
+ // return;
+ //}
+ e.preventDefault ? e.preventDefault() : (e.returnValue = false);
+ e.cancelBubble = true
+ e.returnValue = false;
+
+
+ if (Dec.Utils.jsdiejb.openOption == 1) {
+ Dec.Utils.jsdiejb.openOption = 0;
+ $('.jsdiejb-report-content').css('display', "none");
+ return;
+ }
+ var url = Dec.Utils.jsdiejb.config.reportUrl;
+ var width = Dec.Utils.jsdiejb.config.reportWidth;
+ var height = Dec.Utils.jsdiejb.config.reportHeight;
+ if (width < 0) {
+ width = 800;
+ }
+ if (height < 0) {
+ height = 600;
+ }
+ var widthValue = width + "px";
+ var heightValue = height + "px";
+
+ var left = ($('body').width() - width) / 2;
+ var top = ($('body').height() - height) / 2;
+
+ left = $('.jsdiejb-ball').position().left - width;
+ top = $('.jsdiejb-ball').position().top;
+ if (left < 0) {
+ left = 0;
+ }
+ if (top < 0) {
+ top = 0;
+ }
+ var leftValue = left + "px";
+ var topValue = top + "px";
+ if ((url != undefined) && (url.length > 0)) {
+ Dec.Utils.jsdiejb.openOption = 1;
+ $('.jsdiejb-report-content').css('display', "block");
+ $('.jsdiejb-report-content').css('width', widthValue);
+ $('.jsdiejb-report-content').css('height', heightValue);
+ $('.jsdiejb-report-content').css('left', leftValue);
+ $('.jsdiejb-report-content').css('top', topValue);
+
+ if (Dec.Utils.jsdiejb.openOptionSrc == 0) {
+ Dec.Utils.jsdiejb.openOptionSrc = 1;
+ setTimeout(function () {
+ $('.jsdiejb-report-iframe').attr('src', url);
+ }, 1000 )
+ }
+ }
+
+ return false;
+ },
+ noContextMenu: function (e) {
+ e.preventDefault ? e.preventDefault() : (e.returnValue = false);
+ e.cancelBubble = true
+ e.returnValue = false;
+ return false;
+ }
+});
+
+
+!(function () {
+ var ballImageUrl = Dec.fineServletURL + '/url/jsdiejb/resources?path=jsdiejb/image/ball.png';
+ Dec.Utils.jsdiejb.url = ballImageUrl;
+
+ Dec.Utils.jsdiejb.getConfig(function (e) {
+ Dec.Utils.jsdiejb.config = e;
+ addBall();
+ })
+
+ function addBall() {
+ var reportTitle = Dec.Utils.jsdiejb.config.reportTitle;
+ var iconWidth = Dec.Utils.jsdiejb.config.iconWidth;
+ var reportUrl = Dec.Utils.jsdiejb.config.reportUrl;
+ if (iconWidth < 0) {
+ iconWidth = 40;
+ }
+ var ballContent = '';
+ var reportContent = '';
+ $('body').append(ballContent);
+ $('body').append(reportContent);
+ }
+
+ $(window).resize(function () {
+ Dec.Utils.jsdiejb.resizeBall();
+ });
+})();
\ No newline at end of file
diff --git a/src/main/resources/jsdiejb/image/ball.png b/src/main/resources/jsdiejb/image/ball.png
new file mode 100644
index 0000000..6ffa853
Binary files /dev/null and b/src/main/resources/jsdiejb/image/ball.png differ
diff --git a/悬浮球提示插件使用手册.pdf b/悬浮球提示插件使用手册.pdf
new file mode 100644
index 0000000..840943b
Binary files /dev/null and b/悬浮球提示插件使用手册.pdf differ