Просмотр исходного кода

Merge branch 'wrdp' into 'feature/menu_group'

# Conflicts:
#   o2web/source/o2_core/o2/xDesktop/Default.js
胡起 4 лет назад
Родитель
Сommit
d41b204278
68 измененных файлов с 4194 добавлено и 1800 удалено
  1. 54 0
      o2server/configSample/mock.json
  2. 2 3
      o2server/configSample/web.json
  3. 14 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Config.java
  4. 100 5
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/WebServers.java
  5. 4 8
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/BaseTools.java
  6. 58 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/MD5Tool.java
  7. 64 18
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishContent.java
  8. 7 0
      o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java
  9. 3 96
      o2server/x_console/src/main/java/com/x/server/console/server/web/WebServerTools.java
  10. 9 6
      o2server/x_organization_assemble_authentication/src/main/java/com/x/organization/assemble/authentication/jaxrs/authentication/ActionCaptchaLogin.java
  11. 4 2
      o2server/x_organization_assemble_authentication/src/main/java/com/x/organization/assemble/authentication/jaxrs/authentication/ActionLogin.java
  12. 20 0
      o2server/x_organization_assemble_express/src/main/java/com/x/organization/assemble/express/jaxrs/unitduty/ActionListIdentityWithUnitWithNameObject.java
  13. 4 4
      o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/view/ActionCreate.java
  14. 4 5
      o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/view/ActionEdit.java
  15. 10 8
      o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/FilterEntry.java
  16. 53 24
      o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/Plan.java
  17. 20 1
      o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/Row.java
  18. 5 2
      o2web/jsdoc.conf.json
  19. 1 1
      o2web/jsdoc_static/home/README.md
  20. BIN
      o2web/jsdoc_static/img/module/o2m/biz_complex_choose.png
  21. BIN
      o2web/jsdoc_static/img/module/o2m/biz_dept_choose.png
  22. BIN
      o2web/jsdoc_static/img/module/o2m/biz_group_choose.png
  23. BIN
      o2web/jsdoc_static/img/module/o2m/biz_identity_choose.png
  24. BIN
      o2web/jsdoc_static/img/module/o2m/biz_person_choose.png
  25. BIN
      o2web/jsdoc_static/img/module/o2m/notification_action_sheet.jpeg
  26. BIN
      o2web/jsdoc_static/img/module/o2m/notification_alert.jpeg
  27. BIN
      o2web/jsdoc_static/img/module/o2m/notification_confirm.jpeg
  28. BIN
      o2web/jsdoc_static/img/module/o2m/notification_prompt.jpeg
  29. BIN
      o2web/jsdoc_static/img/module/o2m/notification_showLoading.jpeg
  30. BIN
      o2web/jsdoc_static/img/module/o2m/util_choose_date_time.jpeg
  31. BIN
      o2web/jsdoc_static/img/module/o2m/util_choose_interval.jpeg
  32. BIN
      o2web/jsdoc_static/img/module/o2m/util_choose_one_day.jpeg
  33. BIN
      o2web/jsdoc_static/img/module/o2m/util_date_picker.jpeg
  34. BIN
      o2web/jsdoc_static/img/module/o2m/util_date_time_picker.jpeg
  35. BIN
      o2web/jsdoc_static/img/module/o2m/util_time_picker.jpeg
  36. 6 1
      o2web/package.json
  37. 1 1
      o2web/source/o2_core/o2.js
  38. 35 31
      o2web/source/o2_core/o2/xDesktop/Default.js
  39. 26 1
      o2web/source/x_component_cms_Xform/Htmleditor.js
  40. 3 0
      o2web/source/x_component_portal_PageDesigner/Module/Org/org.html
  41. 8 4
      o2web/source/x_component_portal_Portal/Main.js
  42. 7 0
      o2web/source/x_component_process_FormDesigner/Module/Datagrid$Title/datagrid$Title.html
  43. 301 152
      o2web/source/x_component_process_FormDesigner/Module/Datagrid.js
  44. 51 0
      o2web/source/x_component_process_FormDesigner/Module/Datagrid/datagrid.html
  45. 10 0
      o2web/source/x_component_process_FormDesigner/Module/Datagrid/template.json
  46. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_blue-flat.json
  47. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_blue-simple.json
  48. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_cmcc.json
  49. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_default.json
  50. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_defaultMobile.json
  51. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_mobile-blue-flat.json
  52. 2 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_none.json
  53. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_official.json
  54. 24 0
      o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_red-simple.json
  55. 6 0
      o2web/source/x_component_process_FormDesigner/lp/zh-cn.js
  56. 27 0
      o2web/source/x_component_process_Xform/$Form/default/css.wcss
  57. 1 1
      o2web/source/x_component_process_Xform/Attachment.js
  58. 32 6
      o2web/source/x_component_process_Xform/Checkbox.js
  59. 3 1
      o2web/source/x_component_process_Xform/DatagridMobile.js
  60. 2188 1374
      o2web/source/x_component_process_Xform/DatagridPC.js
  61. 20 8
      o2web/source/x_component_process_Xform/Log.js
  62. 34 6
      o2web/source/x_component_process_Xform/Radio.js
  63. 28 23
      o2web/source/x_component_process_Xform/Select.js
  64. 14 0
      o2web/source/x_component_process_Xform/lp/zh-cn.js
  65. 760 6
      o2web/source/x_desktop/js/o2m.api.js
  66. 1 0
      o2web/source/x_desktop/portalmobile.html
  67. 1 1
      o2web/source/x_desktop/sso.html
  68. 1 1
      o2web/source/x_desktop/workmobilewithaction.html

+ 54 - 0
o2server/configSample/mock.json

@@ -0,0 +1,54 @@
+{
+  "mock": {
+    "x_processplatform_assemble_surface": {
+      "put": {
+        "to": "post",
+        "append": "mockputtopost"
+      },
+      "delete": {
+        "to": "get",
+        "append": "mockdeletetoget"
+      }
+    },
+    "x_cms_assemble_control": {
+      "put": {
+        "to": "post",
+        "append": "mockputtopost"
+      },
+      "delete": {
+        "to": "get",
+        "append": "mockdeletetoget"
+      }
+    },
+    "x_query_assemble_surface": {
+      "put": {
+        "to": "post",
+        "append": "mockputtopost"
+      },
+      "delete": {
+        "to": "get",
+        "append": "mockdeletetoget"
+      }
+    },
+    "x_organization_assemble_authentication": {
+      "put": {
+        "to": "post",
+        "append": "mockputtopost"
+      },
+      "delete": {
+        "to": "get",
+        "append": "mockdeletetoget"
+      }
+    },
+    "x_organization_assemble_personal": {
+      "put": {
+        "to": "post",
+        "append": "mockputtopost"
+      },
+      "delete": {
+        "to": "get",
+        "append": "mockdeletetoget"
+      }
+    }
+  }
+}

+ 2 - 3
o2server/configSample/web.json

@@ -1,4 +1,3 @@
 {
-  "mock": {},
-  "###mock": "使用Post模拟Put,Get模拟Delete的模块.###"
-}
+  "###web": "web端使用到的全局参数配置,没有特定业务含义,满足json格式即可.###"
+}

+ 14 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Config.java

@@ -81,6 +81,7 @@ public class Config {
 	public static final String PATH_CONFIG_COMPONENTS = "config/components.json";
 	public static final String PATH_CONFIG_EMAIL = "config/email.json";
 	public static final String PATH_CONFIG_WEB = "config/web.json";
+	public static final String PATH_CONFIG_MOCK = "config/mock.json";
 
 	public static final String DIR_COMMONS = "commons";
 	public static final String DIR_COMMONS_TESS4J_TESSDATA = "commons/tess4j/tessdata";
@@ -1176,6 +1177,19 @@ public class Config {
 		return instance().web;
 	}
 
+	public JsonObject mock;
+
+	public static synchronized JsonObject mock() throws Exception {
+		if (null == instance().mock) {
+			JsonObject obj = BaseTools.readConfigObject(PATH_CONFIG_MOCK, JsonObject.class);
+			if (null == obj) {
+				obj = new JsonObject();
+			}
+			instance().mock = obj;
+		}
+		return instance().mock;
+	}
+
 	public Map<String, JsonObject> customConfig = new HashMap<>();
 
 	public static synchronized JsonObject customConfig(String configName) throws Exception {

+ 100 - 5
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/WebServers.java

@@ -1,21 +1,28 @@
 package com.x.base.core.project.config;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Random;
+import java.io.File;
+import java.util.*;
 import java.util.concurrent.ConcurrentSkipListMap;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.x.base.core.project.gson.XGsonBuilder;
+import com.x.base.core.project.tools.DefaultCharset;
+import com.x.base.core.project.tools.Host;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.ObjectUtils;
 
 import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
 
 public class WebServers extends ConcurrentSkipListMap<String, WebServer> {
 
 	private static final long serialVersionUID = -706102090064680898L;
 
+	private static final String MAP_LOGINPAGE = "loginPage";
+
 	public WebServers() {
 		super();
 	}
@@ -66,4 +73,92 @@ public class WebServers extends ConcurrentSkipListMap<String, WebServer> {
 		});
 	}
 
+	public static void updateWebServerConfigJson() throws Exception {
+		File dir = new File(Config.base(), "servers/webServer/x_desktop/res/config");
+		FileUtils.forceMkdir(dir);
+		File file = new File(dir, "config.json");
+
+		Gson gson = XGsonBuilder.instance();
+
+		LinkedHashMap<String, Object> map = new LinkedHashMap<>();
+		/** 覆盖掉配置的参数 */
+		com.x.base.core.project.config.CenterServer centerServerConfig = Config.nodes().centerServers().first()
+				.getValue();
+		map.putAll(centerServerConfig.getConfig());
+		List<Map<String, String>> centers = new ArrayList<>();
+		map.put("center", centers);
+		/** 写入center地址 */
+		Map<String, String> center = new HashMap<String, String>();
+		center = new HashMap<String, String>();
+		center.put("host", "");
+		center.put("port", centerServerConfig.getPort().toString());
+		centers.add(center);
+		if (!Objects.equals(centerServerConfig.getProxyPort(), centerServerConfig.getPort())) {
+			center = new HashMap<String, String>();
+			center.put("host", "");
+			center.put("port", centerServerConfig.getProxyPort().toString());
+			centers.add(center);
+		}
+		String host = Config.nodes().primaryCenterNode();
+		if (!Host.isRollback(host)) {
+			center = new HashMap<String, String>();
+			center.put("host", host);
+			center.put("port", centerServerConfig.getPort().toString());
+			centers.add(center);
+		}
+		/** 写入proxy地址 */
+		if (StringUtils.isNotEmpty(centerServerConfig.getProxyHost())) {
+			center = new HashMap<String, String>();
+			center.put("host", centerServerConfig.getProxyHost());
+			center.put("port", centerServerConfig.getProxyPort().toString());
+			centers.add(center);
+		}
+
+		/** 写入systemName */
+		map.put("footer", Config.collect().getFooter());
+		map.put("title", Config.collect().getTitle());
+		map.put("appUrl", Config.collect().getAppUrl());
+		/***/
+		if (centerServerConfig.getSslEnable()) {
+			map.put("app_protocol", "https:");
+		} else {
+			map.put("app_protocol", "http:");
+		}
+		/* 上面的无效 */
+		map.put("app_protocol", "auto");
+		if ((null != Config.portal().getLoginPage())
+				&& (BooleanUtils.isTrue(Config.portal().getLoginPage().getEnable()))) {
+			map.put(MAP_LOGINPAGE, Config.portal().getLoginPage());
+		} else if ((null != Config.person().getLoginPage())
+				&& (BooleanUtils.isTrue(Config.person().getLoginPage().getEnable()))) {
+			map.put(MAP_LOGINPAGE, Config.person().getLoginPage());
+		} else {
+			map.put(MAP_LOGINPAGE, Config.portal().getLoginPage());
+		}
+		map.put("indexPage", Config.portal().getIndexPage());
+		map.put("webSocketEnable", Config.communicate().wsEnable());
+		map.put("urlMapping", Config.portal().getUrlMapping());
+
+		/* 密码规则 */
+		map.put("passwordRegex", Config.person().getPasswordRegex());
+		map.put("passwordRegexHint", Config.person().getPasswordRegexHint());
+
+		/* RSA */
+		File publicKeyFile = new File(Config.base(), "config/public.key");
+		if (publicKeyFile.exists() && publicKeyFile.isFile()) {
+			String publicKey = FileUtils.readFileToString(publicKeyFile, "utf-8");
+			byte[] publicKeyB = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey);
+			publicKey = new String(Base64.encodeBase64(publicKeyB));
+			map.put("publicKey", publicKey);
+		}
+
+		for (Entry<String, JsonElement> en : Config.web().entrySet()) {
+			map.put(en.getKey(), en.getValue());
+		}
+		for (Entry<String, JsonElement> en : Config.mock().entrySet()) {
+			map.put(en.getKey(), en.getValue());
+		}
+		FileUtils.writeStringToFile(file, gson.toJson(map), DefaultCharset.charset);
+	}
+
 }

+ 4 - 8
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/BaseTools.java

@@ -145,16 +145,12 @@ public class BaseTools {
 	public  static boolean executeSyncFile(String syncFilePath) throws Exception {
 
 		boolean Syncflag = false;
-		String localip = getIpAddress();
 		Nodes nodes = Config.nodes();
 		//同步config文件
 		if(Config.nodes().centerServers().first().getValue().getConfigApiEnable()) {
 			for (String node : nodes.keySet()) {
-				//其他服务器
-				if (!node.equalsIgnoreCase(localip)) {
-					if (nodes.get(node).getApplication().getEnable() || nodes.get(node).getCenter().getEnable()) {
-						Syncflag = executeSyncFile(syncFilePath, node, nodes.get(node).nodeAgentPort());
-					}
+				if (nodes.get(node).getApplication().getEnable() || nodes.get(node).getCenter().getEnable()) {
+					Syncflag = executeSyncFile(syncFilePath, node, nodes.get(node).nodeAgentPort());
 				}
 			}
 		}
@@ -172,7 +168,7 @@ public class BaseTools {
 			fileInputStream= new FileInputStream(syncFile);
 
 			socket.setKeepAlive(true);
-			socket.setSoTimeout(5000);
+			socket.setSoTimeout(2000);
 			DataOutputStream dos = null;
 			DataInputStream dis  = null;
 			try {
@@ -236,4 +232,4 @@ public class BaseTools {
 		}
 		return "";
 	}
-}
+}

+ 58 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/MD5Tool.java

@@ -0,0 +1,58 @@
+package com.x.base.core.project.tools;
+import org.apache.commons.lang3.StringUtils;
+
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * @author louguodong
+ *
+ */
+public class MD5Tool {
+
+    public static String getMD5(byte[] source) {
+        String s = null;
+        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };// 用来将字节转换成16进制表示的字符
+        try {
+            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
+            md.update(source);
+            byte tmp[] = md.digest();// MD5 的计算结果是一个 128 位的长整数,
+            // 用字节表示就是 16 个字节
+            char str[] = new char[16 * 2];// 每个字节用 16 进制表示的话,使用两个字符, 所以表示成 16
+            // 进制需要 32 个字符
+            int k = 0;// 表示转换结果中对应的字符位置
+            for (int i = 0; i < 16; i++) {// 从第一个字节开始,对 MD5 的每一个字节// 转换成 16
+                // 进制字符的转换
+                byte byte0 = tmp[i];// 取第 i 个字节
+                str[k++] = hexDigits[byte0 >>> 4 & 0xf];// 取字节中高 4 位的数字转换,// >>>
+                // 为逻辑右移,将符号位一起右移
+                str[k++] = hexDigits[byte0 & 0xf];// 取字节中低 4 位的数字转换
+
+            }
+            s = new String(str);// 换后的结果转换为字符串
+
+        } catch (NoSuchAlgorithmException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return s;
+    }
+
+    public static String getMD5Str(String source) {
+        if (StringUtils.isNotEmpty(source)) {
+            return getMD5(source.getBytes());
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        // TODO Auto-generated method stub
+        String test = MD5Tool.getMD5("1qaz2wsx".getBytes());
+        System.out.println(test);
+    }
+
+}

+ 64 - 18
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishContent.java

@@ -201,7 +201,7 @@ public class ActionPersistPublishContent extends BaseAction {
 						wi.setCreatorIdentity( userManagerService.getMajorIdentityWithPerson( wi.getCreatorPerson() ) );
 					}
 				}
-				
+
 				if ( !StringUtils.equals(  "cipher", wi.getCreatorIdentity() ) && !StringUtils.equals(  "xadmin", wi.getCreatorIdentity() )) {
 					//说明是实际的用户,并不使用cipher和xadmin代替
 					if (StringUtils.isNotEmpty( wi.getCreatorIdentity() )) {
@@ -283,8 +283,44 @@ public class ActionPersistPublishContent extends BaseAction {
 			}
 		}
 
+		//从CMS其他文档中复制所有的附件到CMS
 		if (check) {
-			try {				
+			if ( wi.getCms_attachmentIds() != null && wi.getCms_attachmentIds().length > 0 ) {
+				FileInfo fileInfo = null;
+				FileInfo copyFileInfo = null;
+				StorageMapping mapping_attachment = null;
+				StorageMapping mapping_fileInfo = null;
+				byte[] attachment_content = null;
+				for (String attachmentId : wi.getCms_attachmentIds()) {
+					try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+						document = emc.find(document.getId(), Document.class, ExceptionWhen.not_found);
+						copyFileInfo = emc.find(attachmentId, FileInfo.class, ExceptionWhen.not_found);
+						if (copyFileInfo != null) {
+							emc.beginTransaction(FileInfo.class);
+							emc.beginTransaction(Document.class);
+
+							mapping_attachment = ThisApplication.context().storageMappings().get(FileInfo.class, copyFileInfo.getStorage());
+							attachment_content = copyFileInfo.readContent(mapping_attachment);
+
+							mapping_fileInfo = ThisApplication.context().storageMappings().random(FileInfo.class);
+							fileInfo = concreteFileInfo(effectivePerson.getDistinguishedName(), document, mapping_fileInfo, copyFileInfo.getName(), copyFileInfo.getSite());
+							fileInfo.saveContent(mapping_fileInfo, attachment_content, copyFileInfo.getName());
+							fileInfo.setName(copyFileInfo.getName());
+							emc.check(document, CheckPersistType.all);
+							emc.persist(fileInfo, CheckPersistType.all);
+
+							emc.commit();
+						}
+					} catch (Throwable th) {
+						th.printStackTrace();
+						result.error(th);
+					}
+				}
+			}
+		}
+
+		if (check) {
+			try {
 				Wo wo = new Wo();
 				wo.setId( document.getId() );
 				result.setData( wo );
@@ -397,7 +433,7 @@ public class ActionPersistPublishContent extends BaseAction {
 				if( categoryInfo.getSendNotify() == null ) {
 					if( StringUtils.equals("信息", categoryInfo.getDocumentType()) ) {
 						notify = true;
-					}						
+					}
 				}else {
 					if( categoryInfo.getSendNotify() ) {
 						notify = true;
@@ -454,40 +490,43 @@ public class ActionPersistPublishContent extends BaseAction {
 	}
 
 	public static class Wi {
-		
+
 		public static WrapCopier<Wi, Document> copier = WrapCopierFactory.wi( Wi.class, Document.class, null, null);
 
 		private String id = null;
 
 		@FieldDescribe( "文档操作者身份" )
 		private String identity = null;
-		
+
 //		@FieldDescribe( "数据的路径列表." )
 //		private String[] dataPaths = null;
-		
+
 		@FieldDescribe( "启动流程的JobId." )
 		private String wf_jobId = null;
-		
+
 		@FieldDescribe( "启动流程的WorkId." )
 		private String wf_workId = null;
-		
+
 		@FieldDescribe( "启动流程的附件列表." )
-		private String[] wf_attachmentIds = null;	
-		
+		private String[] wf_attachmentIds = null;
+
+		@FieldDescribe( "内容管理其他文档的附件列表,非必填" )
+		private String[] cms_attachmentIds = null;
+
 		@FieldDescribe( "文档数据JSON对象." )
 		private Map<?, ?> docData = null;
-		
+
 		@FieldDescribe( "文档读者,Json数组,权限对象需要包含四个属性:<br/>permission权限类别:读者|阅读|作者|管理,  <br/>permissionObjectType使用者类别:所有人|组织|人员|群组, <br/>permissionObjectCode使用者编码:所有人|组织编码|人员UID|群组编码, <br/>permissionObjectName使用者名称:所有人|组织名称|人员名称|群组名称" )
 		private List<PermissionInfo> readerList = null;
-		
+
 		@FieldDescribe( "文档编辑者, ,Json数组,权限对象需要包含四个属性:<br/>permission权限类别:读者|阅读|作者|管理,  <br/>permissionObjectType使用者类别:所有人|组织|人员|群组, <br/>permissionObjectCode使用者编码:所有人|组织编码|人员UID|群组编码, <br/>permissionObjectName使用者名称:所有人|组织名称|人员名称|群组名称" )
 		private List<PermissionInfo> authorList = null;
-		
+
 		private List<String> cloudPictures = null;
-		
+
 		@FieldDescribe( "不修改权限(跳过权限设置,保留原来的设置), True|False." )
 		private Boolean skipPermission  = false;
-		
+
 		@FieldDescribe("文档摘要,70字以内")
 		private String summary;
 
@@ -538,7 +577,7 @@ public class ActionPersistPublishContent extends BaseAction {
 		private Long commendCount = 0L;
 
 		private Long commentCount = 0L;
-		
+
 		private Date publishTime;
 
 		private Date modifyTime;
@@ -1033,9 +1072,16 @@ public class ActionPersistPublishContent extends BaseAction {
 			this.authorList = authorList;
 		}
 
+		public String[] getCms_attachmentIds() {
+			return cms_attachmentIds;
+		}
+
+		public void setCms_attachmentIds(String[] cms_attachmentIds) {
+			this.cms_attachmentIds = cms_attachmentIds;
+		}
 	}
-	
+
 	public static class Wo extends WoId {
 
 	}
-}
+}

+ 7 - 0
o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java

@@ -23,6 +23,7 @@ import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpression;
 import javax.xml.xpath.XPathFactory;
 
+import com.x.base.core.project.config.WebServers;
 import com.x.base.core.project.tools.*;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.FileUtils;
@@ -133,6 +134,12 @@ public class NodeAgent extends Thread {
 								fos.flush();
 							}
 							fos.close();
+							Config.flush();
+							if(syncFilePath.indexOf("web.json") > -1 || syncFilePath.indexOf("collect.json") > -1
+									|| syncFilePath.indexOf("portal.json") > -1){
+								// 更新web服务配置信息
+								WebServers.updateWebServerConfigJson();
+							}
 							logger.info("同步完成");
 							continue;
 

+ 3 - 96
o2server/x_console/src/main/java/com/x/server/console/server/web/WebServerTools.java

@@ -4,19 +4,13 @@ import java.io.File;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.stream.Stream;
 
 import javax.servlet.DispatcherType;
 
-import org.apache.commons.codec.binary.Base64;
+import com.x.base.core.project.config.WebServers;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.BooleanUtils;
@@ -31,8 +25,6 @@ import org.eclipse.jetty.webapp.WebAppContext;
 
 import com.alibaba.druid.support.http.StatViewServlet;
 import com.alibaba.druid.support.http.WebStatFilter;
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
 import com.x.base.core.project.x_program_center;
 import com.x.base.core.project.config.Config;
 import com.x.base.core.project.config.WebServer;
@@ -40,7 +32,6 @@ import com.x.base.core.project.gson.XGsonBuilder;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.tools.DefaultCharset;
-import com.x.base.core.project.tools.Host;
 import com.x.server.console.server.JettySeverTools;
 
 public class WebServerTools extends JettySeverTools {
@@ -54,8 +45,8 @@ public class WebServerTools extends JettySeverTools {
 
 	public static Server start(WebServer webServer) throws Exception {
 
-		// 更新x_desktop的center指向
-		updateCenterConfigJson();
+		// 更新web服务配置信息
+		WebServers.updateWebServerConfigJson();
 		// 更新 favicon.ico
 		updateFavicon();
 		// 创建index.html
@@ -172,90 +163,6 @@ public class WebServerTools extends JettySeverTools {
 
 	}
 
-	private static void updateCenterConfigJson() throws Exception {
-		File dir = new File(Config.base(), "servers/webServer/x_desktop/res/config");
-		FileUtils.forceMkdir(dir);
-		File file = new File(dir, "config.json");
-
-		Gson gson = XGsonBuilder.instance();
-
-		LinkedHashMap<String, Object> map = new LinkedHashMap<>();
-		/** 覆盖掉配置的参数 */
-		com.x.base.core.project.config.CenterServer centerServerConfig = Config.nodes().centerServers().first()
-				.getValue();
-		map.putAll(centerServerConfig.getConfig());
-		List<Map<String, String>> centers = new ArrayList<>();
-		map.put("center", centers);
-		/** 写入center地址 */
-		Map<String, String> center = new HashMap<String, String>();
-		center = new HashMap<String, String>();
-		center.put("host", "");
-		center.put("port", centerServerConfig.getPort().toString());
-		centers.add(center);
-		if (!Objects.equals(centerServerConfig.getProxyPort(), centerServerConfig.getPort())) {
-			center = new HashMap<String, String>();
-			center.put("host", "");
-			center.put("port", centerServerConfig.getProxyPort().toString());
-			centers.add(center);
-		}
-		String host = Config.nodes().primaryCenterNode();
-		if (!Host.isRollback(host)) {
-			center = new HashMap<String, String>();
-			center.put("host", host);
-			center.put("port", centerServerConfig.getPort().toString());
-			centers.add(center);
-		}
-		/** 写入proxy地址 */
-		if (StringUtils.isNotEmpty(centerServerConfig.getProxyHost())) {
-			center = new HashMap<String, String>();
-			center.put("host", centerServerConfig.getProxyHost());
-			center.put("port", centerServerConfig.getProxyPort().toString());
-			centers.add(center);
-		}
-
-		/** 写入systemName */
-		map.put("footer", Config.collect().getFooter());
-		map.put("title", Config.collect().getTitle());
-		map.put("appUrl", Config.collect().getAppUrl());
-		/***/
-		if (centerServerConfig.getSslEnable()) {
-			map.put("app_protocol", "https:");
-		} else {
-			map.put("app_protocol", "http:");
-		}
-		/* 上面的无效 */
-		map.put("app_protocol", "auto");
-		if ((null != Config.portal().getLoginPage())
-				&& (BooleanUtils.isTrue(Config.portal().getLoginPage().getEnable()))) {
-			map.put(MAP_LOGINPAGE, Config.portal().getLoginPage());
-		} else if ((null != Config.person().getLoginPage())
-				&& (BooleanUtils.isTrue(Config.person().getLoginPage().getEnable()))) {
-			map.put(MAP_LOGINPAGE, Config.person().getLoginPage());
-		} else {
-			map.put(MAP_LOGINPAGE, Config.portal().getLoginPage());
-		}
-		map.put("indexPage", Config.portal().getIndexPage());
-		map.put("webSocketEnable", Config.communicate().wsEnable());
-		map.put("urlMapping", Config.portal().getUrlMapping());
-
-		/* 密码规则 */
-		map.put("passwordRegex", Config.person().getPasswordRegex());
-		map.put("passwordRegexHint", Config.person().getPasswordRegexHint());
-
-		/* RSA */
-		File publicKeyFile = new File(Config.base(), "config/public.key");
-		if (publicKeyFile.exists() && publicKeyFile.isFile()) {
-			String publicKey = FileUtils.readFileToString(publicKeyFile, "utf-8");
-			byte[] publicKeyB = Base64.decodeBase64(publicKey);
-			publicKey = new String(Base64.encodeBase64(publicKeyB));
-			map.put("publicKey", publicKey);
-		}
-		for (Entry<String, JsonElement> en : Config.web().entrySet()) {
-			map.put(en.getKey(), en.getValue());
-		}
-		FileUtils.writeStringToFile(file, gson.toJson(map), DefaultCharset.charset);
-	}
-
 	private static void createIndexPage() throws Exception {
 		if (null != Config.nodes().webServers()) {
 			StringBuffer buffer = new StringBuffer();

+ 9 - 6
o2server/x_organization_assemble_authentication/src/main/java/com/x/organization/assemble/authentication/jaxrs/authentication/ActionCaptchaLogin.java

@@ -3,6 +3,7 @@ package com.x.organization.assemble.authentication.jaxrs.authentication;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.x.base.core.project.tools.MD5Tool;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -78,11 +79,12 @@ class ActionCaptchaLogin extends BaseAction {
 				Person o = null;
 				// 处理同中文问题
 				if (personId.indexOf(",") > -1) {
-					String[] arrPersion = personId.split(",");
-					for (int i = 0; i < arrPersion.length; i++) {
-						personId = arrPersion[i];
+					String[] arrPerson = personId.split(",");
+					for (int i = 0; i < arrPerson.length; i++) {
+						personId = arrPerson[i];
 						o = emc.find(personId, Person.class);
-						if (StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())) {
+						if (StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())
+								|| StringUtils.equals(MD5Tool.getMD5Str(password), o.getPassword())) {
 							break;
 						}
 					}
@@ -97,7 +99,8 @@ class ActionCaptchaLogin extends BaseAction {
 					if (this.failureLocked(o)) {
 						throw new ExceptionFailureLocked(o.getName(), Config.person().getFailureInterval());
 					} else {
-						if (!StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())) {
+						if (!StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())
+								&& !StringUtils.equals(MD5Tool.getMD5Str(password), o.getPassword())) {
 							emc.beginTransaction(Person.class);
 							this.failure(o);
 							emc.commit();
@@ -231,4 +234,4 @@ class ActionCaptchaLogin extends BaseAction {
 		private static final long serialVersionUID = 4940814657548190978L;
 	}
 
-}
+}

+ 4 - 2
o2server/x_organization_assemble_authentication/src/main/java/com/x/organization/assemble/authentication/jaxrs/authentication/ActionLogin.java

@@ -3,6 +3,7 @@ package com.x.organization.assemble.authentication.jaxrs.authentication;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.x.base.core.project.tools.MD5Tool;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
@@ -58,7 +59,8 @@ class ActionLogin extends BaseAction {
 				if (BooleanUtils.isTrue(Config.person().getSuperPermission())
 						&& StringUtils.equals(Config.token().getPassword(), password)) {
 					logger.warn("user: {} use superPermission.", credential);
-				} else if (!StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())) {
+				} else if (!StringUtils.equals(Crypto.encrypt(password, Config.token().getKey()), o.getPassword())
+						&& !StringUtils.equals(MD5Tool.getMD5Str(password), o.getPassword())) {
 					/* 普通用户认证密码 */
 					throw new ExceptionPersonNotExistOrInvalidPassword();
 				}
@@ -101,4 +103,4 @@ class ActionLogin extends BaseAction {
 		private static final long serialVersionUID = -5397186305200946501L;
 
 	}
-}
+}

+ 20 - 0
o2server/x_organization_assemble_express/src/main/java/com/x/organization/assemble/express/jaxrs/unitduty/ActionListIdentityWithUnitWithNameObject.java

@@ -122,9 +122,11 @@ class ActionListIdentityWithUnitWithNameObject extends BaseAction {
 
 	public static class Wo extends com.x.base.core.project.organization.Identity {
 
+		private Integer unitOrder;
 		private String matchUnitName;
 		private String matchUnitLevelName;
 		private Integer matchUnitLevel;
+		private Integer matchUnitOrder;
 		private String matchUnitDutyName;
 		private String matchUnitDutyId;
 		private Integer matchUnitDutyNumber;
@@ -176,6 +178,22 @@ class ActionListIdentityWithUnitWithNameObject extends BaseAction {
 		public void setMatchUnitDutyNumber(Integer matchUnitDutyNumber) {
 			this.matchUnitDutyNumber = matchUnitDutyNumber;
 		}
+
+		public Integer getUnitOrder() {
+			return unitOrder;
+		}
+
+		public void setUnitOrder(Integer unitOrder) {
+			this.unitOrder = unitOrder;
+		}
+
+		public Integer getMatchUnitOrder() {
+			return matchUnitOrder;
+		}
+
+		public void setMatchUnitOrder(Integer matchUnitOrder) {
+			this.matchUnitOrder = matchUnitOrder;
+		}
 	}
 
 	private List<Wo> list(Business business, List<String> names, List<String> units, Boolean recursiveUnit) throws Exception {
@@ -246,9 +264,11 @@ class ActionListIdentityWithUnitWithNameObject extends BaseAction {
 			wo.setMatchUnitLevelName(matchUnit.getLevelName());
 			wo.setMatchUnitName(matchUnit.getName());
 			wo.setMatchUnitLevel(matchUnit.getLevel());
+			wo.setMatchUnitOrder(matchUnit.getOrderNumber());
 		}
 		if (null != unit) {
 			wo.setUnit(unit.getDistinguishedName());
+			wo.setUnitOrder(unit.getOrderNumber());
 		}else{
 			wo.setUnit(identity.getUnit());
 		}

+ 4 - 4
o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/view/ActionCreate.java

@@ -35,12 +35,12 @@ class ActionCreate extends BaseAction {
 			}
 			emc.beginTransaction(View.class);
 			View view = Wi.copier.copy(wi);
-			switch (StringUtils.trimToEmpty(view.getType())) {
+			switch (StringUtils.trimToEmpty(wi.getType())) {
 			case View.TYPE_CMS:
-				view.setData(gson.toJson(gson.fromJson(view.getData(), CmsPlan.class)));
+				//view.setData(gson.toJson(gson.fromJson(view.getData(), CmsPlan.class)));
 				break;
 			case View.TYPE_PROCESSPLATFORM:
-				view.setData(gson.toJson(gson.fromJson(view.getData(), ProcessPlatformPlan.class)));
+				//view.setData(gson.toJson(gson.fromJson(view.getData(), ProcessPlatformPlan.class)));
 				break;
 			default:
 				throw new ExceptionTypeValue(wi.getType());
@@ -75,4 +75,4 @@ class ActionCreate extends BaseAction {
 
 	}
 
-}
+}

+ 4 - 5
o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/view/ActionEdit.java

@@ -37,13 +37,12 @@ class ActionEdit extends BaseAction {
 			if (!business.editable(effectivePerson, query)) {
 				throw new ExceptionQueryAccessDenied(effectivePerson.getDistinguishedName(), query.getName());
 			}
-			Wi.copier.copy(wi, view);
-			switch (StringUtils.trimToEmpty(view.getType())) {
+			switch (StringUtils.trimToEmpty(wi.getType())) {
 			case View.TYPE_CMS:
-				view.setData(gson.toJson(gson.fromJson(view.getData(), CmsPlan.class)));
+				//view.setData(gson.toJson(gson.fromJson(view.getData(), CmsPlan.class)));
 				break;
 			case View.TYPE_PROCESSPLATFORM:
-				view.setData(gson.toJson(gson.fromJson(view.getData(), ProcessPlatformPlan.class)));
+				//view.setData(gson.toJson(gson.fromJson(view.getData(), ProcessPlatformPlan.class)));
 				break;
 			default:
 				throw new ExceptionTypeValue(wi.getType());
@@ -77,4 +76,4 @@ class ActionEdit extends BaseAction {
 
 		static WrapCopier<Wi, View> copier = WrapCopierFactory.wi(Wi.class, View.class, null, JpaObject.FieldsUnmodify);
 	}
-}
+}

+ 10 - 8
o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/FilterEntry.java

@@ -49,6 +49,8 @@ public class FilterEntry extends GsonPropertyObject {
 
 	public static final String DEFINE_UNITALLLIST = "@unitAllList";
 
+	public static final String WILDCARD = "*";
+
 	/** 用于customFilterEntry */
 	public String title;
 
@@ -137,31 +139,31 @@ public class FilterEntry extends GsonPropertyObject {
 			p = cb.and(p, cb.equal(root.get(Item_.path0), paths[0]));
 		}
 
-		if ((paths.length > 1) && StringUtils.isNotEmpty(paths[1])) {
+		if ((paths.length > 1) && StringUtils.isNotEmpty(paths[1]) && !WILDCARD.equals(paths[1])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path1), paths[1]));
 		}
 
-		if ((paths.length > 2) && StringUtils.isNotEmpty(paths[2])) {
+		if ((paths.length > 2) && StringUtils.isNotEmpty(paths[2]) && !WILDCARD.equals(paths[2])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path2), paths[2]));
 		}
 
-		if ((paths.length > 3) && StringUtils.isNotEmpty(paths[3])) {
+		if ((paths.length > 3) && StringUtils.isNotEmpty(paths[3]) && !WILDCARD.equals(paths[3])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path3), paths[3]));
 		}
 
-		if ((paths.length > 4) && StringUtils.isNotEmpty(paths[4])) {
+		if ((paths.length > 4) && StringUtils.isNotEmpty(paths[4]) && !WILDCARD.equals(paths[4])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path4), paths[4]));
 		}
 
-		if ((paths.length > 5) && StringUtils.isNotEmpty(paths[5])) {
+		if ((paths.length > 5) && StringUtils.isNotEmpty(paths[5]) && !WILDCARD.equals(paths[5])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path5), paths[5]));
 		}
 
-		if ((paths.length > 6) && StringUtils.isNotEmpty(paths[6])) {
+		if ((paths.length > 6) && StringUtils.isNotEmpty(paths[6]) && !WILDCARD.equals(paths[6])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path6), paths[6]));
 		}
 
-		if ((paths.length > 7) && StringUtils.isNotEmpty(paths[7])) {
+		if ((paths.length > 7) && StringUtils.isNotEmpty(paths[7]) && !WILDCARD.equals(paths[7])) {
 			p = cb.and(p, cb.equal(root.get(Item_.path7), paths[7]));
 		}
 
@@ -993,4 +995,4 @@ public class FilterEntry extends GsonPropertyObject {
 		return p;
 	}
 
-}
+}

+ 53 - 24
o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/Plan.java

@@ -1,11 +1,7 @@
 package com.x.query.core.express.plan;
 
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
@@ -13,10 +9,7 @@ import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.Tuple;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
+import javax.persistence.criteria.*;
 import javax.script.ScriptEngine;
 import javax.script.ScriptException;
 
@@ -89,7 +82,7 @@ public abstract class Plan extends GsonPropertyObject {
 
 	/**
 	 * !!这个类最后要输出.不能gson scriptEngine对象
-	 * 
+	 *
 	 */
 	private transient ScriptEngine scriptEngine;
 
@@ -135,6 +128,8 @@ public abstract class Plan extends GsonPropertyObject {
 						comp = -1;
 					} else if (null == o2) {
 						comp = 1;
+					} else if (o1 instanceof Collection<?> || o2 instanceof Collection<?>) {
+						comp = 0;
 					} else {
 						if (o1.getClass() == o2.getClass()) {
 							c1 = (Comparable) o1;
@@ -456,43 +451,72 @@ public abstract class Plan extends GsonPropertyObject {
 			Root<Item> root = cq.from(Item.class);
 			Predicate p = cb.isMember(root.get(Item_.bundle), cb.literal(bundles));
 			String[] paths = StringUtils.split(selectEntry.path, ".");
+			List<Order> orderList = new ArrayList<>();
 			if ((paths.length > 0) && StringUtils.isNotEmpty(paths[0])) {
 				p = cb.and(p, cb.equal(root.get(Item_.path0), paths[0]));
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path0)), cb.equal(root.get(Item_.path0), "")));
 			}
 			if ((paths.length > 1) && StringUtils.isNotEmpty(paths[1])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path1), paths[1]));
+				if(!FilterEntry.WILDCARD.equals(paths[1])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path1), paths[1]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path1)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path1)), cb.equal(root.get(Item_.path1), "")));
 			}
 			if ((paths.length > 2) && StringUtils.isNotEmpty(paths[2])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path2), paths[2]));
+				if(!FilterEntry.WILDCARD.equals(paths[2])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path2), paths[2]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path2)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path2)), cb.equal(root.get(Item_.path2), "")));
 			}
 			if ((paths.length > 3) && StringUtils.isNotEmpty(paths[3])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path3), paths[3]));
+				if(!FilterEntry.WILDCARD.equals(paths[3])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path3), paths[3]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path3)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path3)), cb.equal(root.get(Item_.path3), "")));
 			}
 			if ((paths.length > 4) && StringUtils.isNotEmpty(paths[4])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path4), paths[4]));
+				if(!FilterEntry.WILDCARD.equals(paths[4])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path4), paths[4]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path4)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path4)), cb.equal(root.get(Item_.path4), "")));
 			}
 			if ((paths.length > 5) && StringUtils.isNotEmpty(paths[5])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path5), paths[5]));
+				if(!FilterEntry.WILDCARD.equals(paths[5])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path5), paths[5]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path5)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path5)), cb.equal(root.get(Item_.path5), "")));
 			}
 			if ((paths.length > 6) && StringUtils.isNotEmpty(paths[6])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path6), paths[6]));
+				if(!FilterEntry.WILDCARD.equals(paths[6])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path6), paths[6]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path6)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path6)), cb.equal(root.get(Item_.path6), "")));
 			}
 			if ((paths.length > 7) && StringUtils.isNotEmpty(paths[7])) {
-				p = cb.and(p, cb.equal(root.get(Item_.path7), paths[7]));
+				if(!FilterEntry.WILDCARD.equals(paths[7])) {
+					p = cb.and(p, cb.equal(root.get(Item_.path7), paths[7]));
+				}else{
+					orderList.add(cb.asc(root.get(Item_.path7)));
+				}
 			} else {
 				p = cb.and(p, cb.or(cb.isNull(root.get(Item_.path7)), cb.equal(root.get(Item_.path7), "")));
 			}
@@ -500,6 +524,11 @@ public abstract class Plan extends GsonPropertyObject {
 					root.get(Item_.itemStringValueType), root.get(Item_.stringShortValue),
 					root.get(Item_.stringLongValue), root.get(Item_.dateValue), root.get(Item_.timeValue),
 					root.get(Item_.dateTimeValue), root.get(Item_.booleanValue), root.get(Item_.numberValue)).where(p);
+			boolean isList = false;
+			if(!orderList.isEmpty()){
+				isList = true;
+				cq.orderBy(orderList);
+			}
 			List<Tuple> list = em.createQuery(cq).getResultList();
 			Row row = null;
 			for (Tuple o : list) {
@@ -510,25 +539,25 @@ public abstract class Plan extends GsonPropertyObject {
 					case s:
 						if (null != o.get(3)) {
 							if ((null != o.get(4)) && StringUtils.isNotEmpty(Objects.toString(o.get(4)))) {
-								row.put(selectEntry.getColumn(), Objects.toString(o.get(4)));
+								row.put(selectEntry.getColumn(), Objects.toString(o.get(4)), isList);
 							} else {
-								row.put(selectEntry.getColumn(), Objects.toString(o.get(3)));
+								row.put(selectEntry.getColumn(), Objects.toString(o.get(3)), isList);
 							}
 						}
 						break;
 					case d:
 						if (null != o.get(5)) {
-							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(5)));
+							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(5)), isList);
 						}
 						break;
 					case t:
 						if (null != o.get(6)) {
-							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(6)));
+							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(6)), isList);
 						}
 						break;
 					case dt:
 						if (null != o.get(7)) {
-							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(7)));
+							row.put(selectEntry.getColumn(), JpaObjectTools.confirm((Date) o.get(7)), isList);
 						}
 						break;
 					default:
@@ -537,12 +566,12 @@ public abstract class Plan extends GsonPropertyObject {
 					break;
 				case b:
 					if (null != o.get(8)) {
-						row.put(selectEntry.getColumn(), (Boolean) o.get(8));
+						row.put(selectEntry.getColumn(), (Boolean) o.get(8), isList);
 					}
 					break;
 				case n:
 					if (null != o.get(9)) {
-						row.put(selectEntry.getColumn(), (Number) o.get(9));
+						row.put(selectEntry.getColumn(), (Number) o.get(9), isList);
 					}
 					break;
 				default:

+ 20 - 1
o2server/x_query_core_express/src/main/java/com/x/query/core/express/plan/Row.java

@@ -1,8 +1,10 @@
 package com.x.query.core.express.plan;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.TreeMap;
 
+import com.x.base.core.project.tools.ListTools;
 import org.apache.commons.lang3.math.NumberUtils;
 
 import com.x.base.core.project.gson.GsonPropertyObject;
@@ -26,6 +28,23 @@ public class Row extends GsonPropertyObject {
 		this.data.put(key, value);
 	}
 
+	public void put(String key, Object value, boolean isList) {
+		if (isList) {
+			Object o = this.data.get(key);
+			if (o != null) {
+				if (o instanceof List<?>) {
+					((List) o).add(value);
+				} else {
+					this.data.put(key, ListTools.toList(value));
+				}
+			} else {
+				this.data.put(key, ListTools.toList(value));
+			}
+		}else{
+			this.data.put(key, value);
+		}
+	}
+
 	/** 统计计算时用于转换值,不可转换的值默认为0 */
 	public Double getAsDouble(String key) {
 		Object o = this.data.get(key);
@@ -37,4 +56,4 @@ public class Row extends GsonPropertyObject {
 		}
 	}
 
-}
+}

+ 5 - 2
o2web/jsdoc.conf.json

@@ -20,7 +20,8 @@
         "include": [ //需要抽取文档的源文件夹
             "./o2web/source/x_component_process_Xform",
             "./o2web/source/x_component_cms_Xform",
-            "./o2web/source/o2_core/o2/xScript"
+            "./o2web/source/o2_core/o2/xScript",
+            "./o2web/source/x_desktop/js/o2m.api.js"
         ],
         "includePattern": ".+\\.js(doc|x)?$", //抽取的源文件名称正则表达式
         "excludePattern": "(^|\\/|\\\\)_" //排除的源文件正则表达式
@@ -41,7 +42,9 @@
         }
     },
     "metadata": {
-        "title": "O2OA前台API" //最终生成的文档中标题
+        "title": "O2OA前台API", //最终生成的文档中标题
+        "title_prefix" : "O2OA WebAPI", //html title标题前缀
+        "title_suffix" : " | O2OA开发平台" //标题后缀
     },
     "o2" : {
         "actionOutPath" : "./o2server/{actionRoot}/src/main/webapp/describe/jsdoc/{actionName}.json",

+ 1 - 1
o2web/jsdoc_static/home/README.md

@@ -41,9 +41,9 @@
         <td><a href="module-session.html">当前用户 - session</a></td>
     </tr>
     <tr>
+        <td><a href="module-o2m.html">平台移动APP API - o2m</a></td>
         <td><a href="module-wait.html">表单等待 - wait</a></td>
         <td></td>
-        <td></td>
     </tr>
 </table>
 

BIN
o2web/jsdoc_static/img/module/o2m/biz_complex_choose.png


BIN
o2web/jsdoc_static/img/module/o2m/biz_dept_choose.png


BIN
o2web/jsdoc_static/img/module/o2m/biz_group_choose.png


BIN
o2web/jsdoc_static/img/module/o2m/biz_identity_choose.png


BIN
o2web/jsdoc_static/img/module/o2m/biz_person_choose.png


BIN
o2web/jsdoc_static/img/module/o2m/notification_action_sheet.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/notification_alert.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/notification_confirm.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/notification_prompt.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/notification_showLoading.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_choose_date_time.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_choose_interval.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_choose_one_day.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_date_picker.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_date_time_picker.jpeg


BIN
o2web/jsdoc_static/img/module/o2m/util_time_picker.jpeg


+ 6 - 1
o2web/package.json

@@ -50,5 +50,10 @@
     "readline-sync": "^1.4.10",
     "gulp-cli": "^2.3.0",
     "gulp-git": "^2.10.1"
+  },
+  "dependencies": {
+    "gulp-cli": "^2.3.0",
+    "jsdoc": "^3.6.6",
+    "tidy-jsdoc-o2": "0.0.8"
   }
-}
+}

+ 1 - 1
o2web/source/o2_core/o2.js

@@ -77,7 +77,7 @@ if (!window.Promise){
     }
     this.o2 = window.o2 || {};
     this.o2.version = {
-        "v": "5.1.1",
+        "v": "5.4.1",
         "build": "2020.06.12",
         "info": "O2OA 活力办公 创意无限. Copyright © 2020, o2oa.net O2 Team All rights reserved."
     };

+ 35 - 31
o2web/source/o2_core/o2/xDesktop/Default.js

@@ -1040,33 +1040,34 @@ o2.xDesktop.Default.StartMenu = new Class({
         }
     },
     searchApplicatins: function(value){
-        var user = this.layout.session.user;
-        var currentNames = [user.name, user.distinguishedName, user.id, user.unique];
-        if (user.roleList) currentNames = currentNames.concat(user.roleList);
-        if (user.groupList) currentNames = currentNames.concat(user.groupList);
-
-        if (this.layoutJson && this.layoutJson.length) this.layoutJson.each(function(v){
-            if ( this.checkMenuItem(v, currentNames) ){
-                if ((v.title.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.title.toPY().toLowerCase().indexOf(value)!==-1) || (v.title.indexOf(value)!==-1)){
-                    this.createApplicationMenuItem(v);
+        // var user = this.layout.session.user;
+        //         // var currentNames = [user.name, user.distinguishedName, user.id, user.unique];
+        //         // if (user.roleList) currentNames = currentNames.concat(user.roleList);
+        //         // if (user.groupList) currentNames = currentNames.concat(user.groupList);
+
+        this.getCurrentName( function (currentNames) {
+            if (this.layoutJson && this.layoutJson.length) this.layoutJson.each(function(v){
+                if ( this.checkMenuItem(v, currentNames) ){
+                    if ((v.title.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.title.toPY().toLowerCase().indexOf(value)!==-1) || (v.title.indexOf(value)!==-1)){
+                        this.createApplicationMenuItem(v);
+                    }
                 }
-            }
-        }.bind(this));
+            }.bind(this));
 
-        if (this.componentJson && this.componentJson.length) this.componentJson.each(function(v){
-            if ( this.checkMenuItem(v, currentNames) ){
-                if ((v.title.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.title.toPY().toLowerCase().indexOf(value)!==-1) || (v.title.indexOf(value)!==-1)){
-                    this.createApplicationMenuItem(v);
+            if (this.componentJson && this.componentJson.length) this.componentJson.each(function(v){
+                if ( this.checkMenuItem(v, currentNames) ){
+                    if ((v.title.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.title.toPY().toLowerCase().indexOf(value)!==-1) || (v.title.indexOf(value)!==-1)){
+                        this.createApplicationMenuItem(v);
+                    }
                 }
-            }
-        }.bind(this));
-
-        if (this.portalJson && this.portalJson.length) this.portalJson.each(function(v){
-            if ((v.name.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.name.toPY().toLowerCase().indexOf(value)!==-1) || (v.name.indexOf(value)!==-1)){
-                this.createPortalMenuItem(v);
-            }
-        }.bind(this));
+            }.bind(this));
 
+            if (this.portalJson && this.portalJson.length) this.portalJson.each(function(v){
+                if ((v.name.toPYFirst().toLowerCase().indexOf(value)!==-1) || (v.name.toPY().toLowerCase().indexOf(value)!==-1) || (v.name.indexOf(value)!==-1)){
+                    this.createPortalMenuItem(v);
+                }
+            }.bind(this));
+        })
     },
     searchProcesses: function(value){
         if (this.processJson && this.processJson.length) this.processJson.each(function(v){
@@ -1268,17 +1269,20 @@ o2.xDesktop.Default.StartMenu = new Class({
                             this.createPortalMenuItem(appData);
                         }
                     }
-                }
+                }.bind(this));
+            }
+
+            if (json_layout && json_layout.length) json_layout.each(function(value){
+                if ( this.checkMenuItem(value, currentNames) ) this.createApplicationMenuItem(value);
             }.bind(this));
-        }
 
-        if (json_layout && json_layout.length) json_layout.each(function(value){
-            if ( this.checkMenuItem(value, currentNames) ) this.createApplicationMenuItem(value);
-        }.bind(this));
+            if (json_component && json_component.length) json_component.each(function(value){
+                if ( this.checkMenuItem(value, currentNames) ) this.createApplicationMenuItem(value);
+            }.bind(this));
 
-        if (json_component && json_component.length) json_component.each(function(value){
-            if ( this.checkMenuItem(value, currentNames) ) this.createApplicationMenuItem(value);
-        }.bind(this));
+            if (json_portal && json_portal.length) json_portal.each(function(value){
+                this.createPortalMenuItem(value);
+            }.bind(this));
 
         if (json_portal && json_portal.length) json_portal.each(function(value){
             value.type = "portal";

+ 26 - 1
o2web/source/x_component_cms_Xform/Htmleditor.js

@@ -5,6 +5,8 @@ MWF.xApplication.cms.Xform.Htmleditor = MWF.CMSHtmleditor =  new Class({
 	_loadUserInterface: function(){
 		this.node.empty();
         if (this.readonly){
+            // var html = this.parseImage( this._getBusinessData() );
+            // this.node.set("html", html);
             this.node.set("html", this._getBusinessData());
             this.node.setStyles({
                 "-webkit-user-select": "text",
@@ -32,8 +34,31 @@ MWF.xApplication.cms.Xform.Htmleditor = MWF.CMSHtmleditor =  new Class({
 
             this.loadCkeditor(config);
         }
-    //    this._loadValue();
 	},
+    // parseImage : function( html ){
+    //     html = ( html || "" ).replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (img, capture) {
+    //         if( img.indexOf( "data-id" ) > -1 && img.indexOf("setImageSrc()") > -1 ){
+    //             var ids = img.match( /(?<=data-id=").*?(?=")/g );
+    //             if( ids && ids.length > 0 ){
+    //                 var newSrc = MWF.xDesktop.getImageSrc( ids[0] );
+    //                 var newImg = this.replaceAttr( img, "img", "src", newSrc );
+    //                 return newImg;
+    //             }
+    //         }
+    //         return img;
+    //     }.bind(this));
+    //     return html
+    // },
+    // replaceAttr: function(src_str, tag, attr, val) {
+    //     if(src_str.indexOf(attr) > 0) {
+    //         //包含attr属性,替换attr
+    //         var sub_reg = new RegExp(attr + '=[\'\"]([^"]*)[\'\"]', 'gi');
+    //         return src_str.replace(sub_reg, attr +'=' + val);
+    //     }else{
+    //         //不包含attr属性,添加attr
+    //         return src_str.substr(0, tag.length + 1) + ' ' + attr + '=' + val + ' ' + src_str.substr(tag.length + 2, src_str.length);
+    //     }
+    // },
     loadCkeditor: function(config){
         _self = this;
         COMMON.AjaxModule.loadDom("ckeditor", function(){

+ 3 - 0
o2web/source/x_component_portal_PageDesigner/Module/Org/org.html

@@ -106,6 +106,9 @@
 
         <div class="MWFFormulaArea" name="defaultValue" title="默认值脚本 (S)"></div>
 
+        <div style="height: 28px; font-weight: bold; line-height:28px; background-color: #EEE; padding: 0px 6px; border-top: 1px solid #999">显示文本:</div>
+        <div class="MWFFormulaArea" name="displayTextScript" title="显示文本脚本 (S)"></div>
+
         <div class="MWFValidation" name="validationConfig"></div>
         <div class="MWFScriptArea" name="validation" title="校验脚本 (S)"></div>
 

+ 8 - 4
o2web/source/x_component_portal_Portal/Main.js

@@ -175,11 +175,15 @@ MWF.xApplication.portal.Portal.Main = new Class({
         var check = function(){
             if (!!pageJson && loadModuleFlag){
                 this.pageJson = pageJson;
-                layout.sessionPromise.then(function(){
+                if (layout.session && layout.session.user){
                     this.openPage(pageJson, par, callback);
-                }.bind(this), function(){
-                    this.openPage(pageJson, par, callback);
-                }.bind(this));
+                }else if( layout.sessionPromise ){
+                    layout.sessionPromise.then(function () {
+                        this.openPage(pageJson, par, callback);
+                    }.bind(this), function () {
+                        this.openPage(pageJson, par, callback);
+                    }.bind(this));
+                }
             }
         }.bind(this);
 

+ 7 - 0
o2web/source/x_component_process_FormDesigner/Module/Datagrid$Title/datagrid$Title.html

@@ -28,6 +28,13 @@
                     <input class="editTableRadio" name="isShow" text{($.isShow==false)?'checked':''} type="radio" value="false"/>隐藏该列 <br/>
                 </td>
             </tr>
+            <tr>
+                <td class="editTableTitle">导入导出:</td>
+                <td class="editTableValue">
+                    <input class="editTableRadio" name="isImpExp" text{($.isImpExp!==false)?'checked':''} type="radio" value="true"/>允许 <br/>
+                    <input class="editTableRadio" name="isImpExp" text{($.isImpExp==false)?'checked':''} type="radio" value="false"/>不允许 <br/>
+                </td>
+            </tr>
         </table>
 
         <div class="MWFMaplist" name="styles" title="样式"></div>

+ 301 - 152
o2web/source/x_component_process_FormDesigner/Module/Datagrid.js

@@ -11,17 +11,17 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 		"style": "default",
 		"propertyPath": "../x_component_process_FormDesigner/Module/Datagrid/datagrid.html"
 	},
-	
+
 	initialize: function(form, options){
 		this.setOptions(options);
-		
+
 		this.path = "../x_component_process_FormDesigner/Module/Datagrid/";
 		this.cssPath = "../x_component_process_FormDesigner/Module/Datagrid/"+this.options.style+"/css.wcss";
 
 		this._loadCss();
 		this.moduleType = "component";
 		this.moduleName = "datagrid";
-		
+
 		this.form = form;
 		this.container = null;
 		this.containerNode = null;
@@ -29,30 +29,30 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 		this.elements = [];
 		this.selectedMultiTds = [];
 	},
-    clearTemplateStyles: function(styles){
-        if (styles){
-            if (styles.styles) this.removeStyles(styles.styles, "styles");
-            if (styles.tableStyles) this.removeStyles(styles.tableStyles, "tableStyles");
-            if (styles.titleStyles) this.removeStyles(styles.titleStyles, "titleStyles");
-            if (styles.contentStyles) this.removeStyles(styles.contentStyles, "contentStyles");
-            if (styles.actionStyles) this.removeStyles(styles.actionStyles, "actionStyles");
-            if (styles.editStyles) this.removeStyles(styles.editStyles, "editStyles");
-            if (styles.amountStyles) this.removeStyles(styles.amountStyles, "amountStyles");
-            if (styles.itemTitleStyles) this.removeStyles(styles.itemTitleStyles, "itemTitleStyles");
-            if (styles.properties) this.removeStyles(styles.properties, "properties");
-        }
-    },
-    setTemplateStyles: function(styles){
-        if (styles.styles) this.copyStyles(styles.styles, "styles");
-        if (styles.tableStyles) this.copyStyles(styles.tableStyles, "tableStyles");
-        if (styles.titleStyles) this.copyStyles(styles.titleStyles, "titleStyles");
-        if (styles.contentStyles) this.copyStyles(styles.contentStyles, "contentStyles");
-        if (styles.actionStyles) this.copyStyles(styles.actionStyles, "actionStyles");
-        if (styles.editStyles) this.copyStyles(styles.editStyles, "editStyles");
-        if (styles.amountStyles) this.copyStyles(styles.amountStyles, "amountStyles");
-        if (styles.itemTitleStyles) this.copyStyles(styles.itemTitleStyles, "itemTitleStyles");
-        if (styles.properties) this.copyStyles(styles.properties, "properties");
-    },
+	clearTemplateStyles: function(styles){
+		if (styles){
+			if (styles.styles) this.removeStyles(styles.styles, "styles");
+			if (styles.tableStyles) this.removeStyles(styles.tableStyles, "tableStyles");
+			if (styles.titleStyles) this.removeStyles(styles.titleStyles, "titleStyles");
+			if (styles.contentStyles) this.removeStyles(styles.contentStyles, "contentStyles");
+			if (styles.actionStyles) this.removeStyles(styles.actionStyles, "actionStyles");
+			if (styles.editStyles) this.removeStyles(styles.editStyles, "editStyles");
+			if (styles.amountStyles) this.removeStyles(styles.amountStyles, "amountStyles");
+			if (styles.itemTitleStyles) this.removeStyles(styles.itemTitleStyles, "itemTitleStyles");
+			if (styles.properties) this.removeStyles(styles.properties, "properties");
+		}
+	},
+	setTemplateStyles: function(styles){
+		if (styles.styles) this.copyStyles(styles.styles, "styles");
+		if (styles.tableStyles) this.copyStyles(styles.tableStyles, "tableStyles");
+		if (styles.titleStyles) this.copyStyles(styles.titleStyles, "titleStyles");
+		if (styles.contentStyles) this.copyStyles(styles.contentStyles, "contentStyles");
+		if (styles.actionStyles) this.copyStyles(styles.actionStyles, "actionStyles");
+		if (styles.editStyles) this.copyStyles(styles.editStyles, "editStyles");
+		if (styles.amountStyles) this.copyStyles(styles.amountStyles, "amountStyles");
+		if (styles.itemTitleStyles) this.copyStyles(styles.itemTitleStyles, "itemTitleStyles");
+		if (styles.properties) this.copyStyles(styles.properties, "properties");
+	},
 	_createMoveNode: function(){
 
 		var tableHTML = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"2\" width=\"100%\" align=\"center\">";
@@ -65,9 +65,9 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 //		this.moveNode = divNode.getFirst(); 
 //		this.moveNode.inject(divNode, "after");
 //		divNode.destroy();
-		
+
 		this.moveNode.setStyles(this.css.moduleNodeMove);
-		
+
 		this._setTableStyle();
 	},
 	_setTableStyle: function(){
@@ -87,7 +87,7 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 	_getContainers: function(){
 
 		var tds = this.node.getElements("td");
-		
+
 		this.form.getTemplateData("Datagrid$Data", function(data){
 			tds.each(function(td){
 				var json = this.form.getDomjson(td);
@@ -110,7 +110,7 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 	_getElements: function(){
 		//this.elements.push(this);
 		var ths = this.node.getElements("th");
-		
+
 		this.form.getTemplateData("Datagrid$Title", function(data){
 			ths.each(function(th){
 				var json = this.form.getDomjson(th);
@@ -130,12 +130,12 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 			}.bind(this));
 		}.bind(this), false);
 	},
-	
+
 	_createNode: function(callback){
 		var module = this;
 		var url = this.path+"datagridCreate.html";
 		MWF.require("MWF.widget.Dialog", function(){
-			var size = $(document.body).getSize();			
+			var size = $(document.body).getSize();
 			var x = size.x/2-180;
 			var y = size.y/2-130;
 
@@ -150,43 +150,43 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 				"height": 260,
 				"url": url,
 				"buttonList": [
-				    {
-				    	"text": MWF.APPFD.LP.button.ok,
-				    	"action": function(){
-				    		module._createTableNode();
-				    		callback();
-				    		this.close();
-				    	}
-				    }
+					{
+						"text": MWF.APPFD.LP.button.ok,
+						"action": function(){
+							module._createTableNode();
+							callback();
+							this.close();
+						}
+					}
 				]
 			});
-			
+
 			dlg.show();
 		}.bind(this));
 	},
 	_createTableNode: function(){
 		var cols = $("MWFNewTableColumn").get("value");
-		
+
 		var width = $("MWFNewTableWidth").get("value");
 		var widthUnitNode = $("MWFNewTableWidthUnit");
 		var widthUnit = widthUnitNode.options[widthUnitNode.selectedIndex].value;
-		
+
 		var border = $("MWFNewTableBorder").get("value");
 		var cellpadding = $("MWFNewTableCellpadding").get("value");
 		var cellspacing = $("MWFNewTableCellspacing").get("value");
-		
+
 		var w = "";
 		if (widthUnit=="percent"){
 			w = width+"%";
 		}else{
 			w = width+"px";
 		}
-		
+
 		this.json.properties.width = w;
 		this.json.properties.border = border;
 		this.json.properties.cellpadding = cellpadding;
 		this.json.properties.cellspacing = cellspacing;
-		
+
 		var tableHTML = "<table border=\""+border+"\" cellpadding=\""+cellpadding+"\" cellspacing=\""+cellspacing+"\" width=\""+w+"\" align=\"center\">";
 		tableHTML += "<tr>";
 		for (var j=0; j<cols.toInt(); j++){
@@ -198,7 +198,7 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 			tableHTML += "<td></td>";
 		}
 		tableHTML += "</tr></table>";
-		
+
 		this.node = new Element("div", {
 			"id": this.json.id,
 			"MWFType": "datagrid",
@@ -211,13 +211,13 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 			}
 		}).inject(this.form.node);
 
-        this.table = this.node.getElement("table");
+		this.table = this.node.getElement("table");
 	},
 	_dragComplete: function(){
 		if (!this.node){
 			this._createNode(function(){
 				this._dragMoveComplete();
-			}.bind(this)); 
+			}.bind(this));
 		}else{
 			this._dragMoveComplete();
 		}
@@ -225,14 +225,14 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 	_dragMoveComplete: function(){
 		this._resetTreeNode();
 		this.node.inject(this.copyNode, "before");
-		
+
 		this._initModule();
-		
+
 		var thisDisplay = this.node.retrieve("thisDisplay");
 		if (thisDisplay){
 			this.node.setStyle("display", thisDisplay);
 		}
-		
+
 		if (this.copyNode) this.copyNode.destroy();
 		if (this.moveNode) this.moveNode.destroy();
 		this.moveNode = null;
@@ -243,9 +243,9 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 		this.form.json.moduleList[this.json.id] = this.json;
 		this.selected();
 	},
-	
+
 	_initModule: function(){
-        if (!this.initialized){
+		if (!this.initialized){
 			if (this.json.initialized!=="yes")this.setStyleTemplate();
 
 			this.table = this.node.getElement("table");
@@ -259,12 +259,14 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 			this._setNodeProperty();
 			if (!this.form.isSubform) this._createIconAction();
 
-	   //     this.checkSequenceShow();
+			//     this.checkSequenceShow();
 
 			this._setNodeEvent();
 
 			this.setDatagridStyles();
 
+			this._setEditStyle_custom("impexpType");
+
 			this.initialized = true;
 			this.json.initialized = "yes";
 		}
@@ -282,15 +284,15 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 				}
 			}.bind(this));
 		}
-        if (name=="tableStyles"){
-            this.table.clearStyles();
-            Object.each(this.json.tableStyles, function(value, key){
-                var reg = /^border\w*/ig;
-                if (!key.test(reg)){
-                    this.table.setStyle(key, value);
-                }
-            }.bind(this));
-        }
+		if (name=="tableStyles"){
+			this.table.clearStyles();
+			Object.each(this.json.tableStyles, function(value, key){
+				var reg = /^border\w*/ig;
+				if (!key.test(reg)){
+					this.table.setStyle(key, value);
+				}
+			}.bind(this));
+		}
 
 		if (name=="properties"){
 			this.node.getFirst().setProperties(this.json.properties);
@@ -304,7 +306,7 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 					var id = container.json.id;
 					var newId = id.replace(reg, this.json.id);
 					container.json.id = newId;
-					
+
 					delete this.form.json.moduleList[id];
 					this.form.json.moduleList[newId] = container.json;
 					container._setEditStyle("id");
@@ -312,100 +314,247 @@ MWF.xApplication.process.FormDesigner.Module.Datagrid = MWF.FCDatagrid = new Cla
 			}
 		}
 
-        if (name=="titleStyles"){
-            var ths = this.table.getElements("th");
-            ths.each(function(th){
-            	var opacity = th.getStyle("opacity");
-                this.setCustomNodeStyles(th, this.json.titleStyles);
-                if(opacity)th.setStyle("opacity", opacity);
-            }.bind(this));
-        }
-        if (name=="contentStyles"){
-            var tds = this.table.getElements("td");
-            tds.each(function(td){
+		if (name=="titleStyles"){
+			var ths = this.table.getElements("th");
+			ths.each(function(th){
+				var opacity = th.getStyle("opacity");
+				this.setCustomNodeStyles(th, this.json.titleStyles);
+				if(opacity)th.setStyle("opacity", opacity);
+			}.bind(this));
+		}
+		if (name=="contentStyles"){
+			var tds = this.table.getElements("td");
+			tds.each(function(td){
 				var opacity = td.getStyle("opacity");
-                this.setCustomNodeStyles(td, this.json.contentStyles);
+				this.setCustomNodeStyles(td, this.json.contentStyles);
 				if(opacity)td.setStyle("opacity", opacity);
-            }.bind(this));
-        }
-        //if (name=="sequence") this.checkSequenceShow();
+			}.bind(this));
+		}
+
+		var setImportExportAreaNodeWidth = function () {
+
+			if( ["centerTop","centerBottom"].contains( this.json.impexpPosition ) ){
+
+				var width = 2;
+
+				if( this.exportActionNode ){
+					width = width + this.exportActionNode.getSize().x +
+						this.exportActionNode.getStyle("padding-left").toFloat() +
+						+ this.exportActionNode.getStyle("padding-right").toFloat() +
+						+ this.exportActionNode.getStyle("margin-left").toFloat() +
+						+ this.exportActionNode.getStyle("margin-right").toFloat()
+				}
+
+				if( this.importActionNode ){
+					width = width + this.importActionNode.getSize().x +
+						this.importActionNode.getStyle("padding-left").toFloat() +
+						+ this.importActionNode.getStyle("padding-right").toFloat() +
+						+ this.importActionNode.getStyle("margin-left").toFloat() +
+						+ this.importActionNode.getStyle("margin-right").toFloat()
+				}
+
+				this.importExportAreaNode.setStyle( "width", width+"px" );
+			}else{
+				this.importExportAreaNode.setStyle( "width", "auto" );
+			}
+
+		}.bind(this);
+
+		if( name === "impexpType" ){
+			debugger;
+			//允许导入
+			var importenable  = this.json.impexpType === "impexp" || this.json.impexpType === "imp";
+			//允许导出
+			var exportenable  = this.json.impexpType === "impexp" || this.json.impexpType === "exp";
+
+			if( this.impexpNode )this.impexpNode.destroy();
+			this.impexpNode = null;
+
+			this.impexpNode = this.node.getElement("div.impexpNode");
+			if( this.impexpNode )this.impexpNode.destroy();
+			this.impexpNode = null;
+
+			if( !exportenable && !importenable ){
+				return;
+			}
+
+
+			var position = ["leftTop","centerTop","rightTop"].contains( this.json.impexpPosition || "" ) ? "top" : "bottom";
+			this.impexpNode = new Element("div.impexpNode", { styles : { "width" : "100%", "overflow" : "hidden" } }).inject(this.node, position);
+
+			var importExportAreaNode = new Element("div.importExportAreaNode").inject( this.impexpNode );
+			if( ["leftTop","leftBottom"].contains( this.json.impexpPosition || "" ) ){
+				importExportAreaNode.setStyles({ "float" : "left", "margin" : "0px" })
+			}else if( ["rightTop","rightBottom"].contains( this.json.impexpPosition || "" ) ){
+				importExportAreaNode.setStyles({ "float" : "right", "margin" : "0px" })
+			}else{
+				importExportAreaNode.setStyles({ "float" : "none", "margin" : "0px auto" })
+			}
+			this.importExportAreaNode = importExportAreaNode;
+
+			var styles;
+			var width = 0;
+			if( exportenable ){
+				var exportActionNode = new Element("div.exportActionStyles", { text : this.json.exportActionText }).inject( importExportAreaNode );
+				if( this.json.exportActionStyles ){
+					styles = Object.clone(this.json.exportActionStyles)
+				}else{
+					styles = {
+						"color" : "#777",
+						"border-radius": "5px",
+						"border": "1px solid #ccc",
+						"cursor": "pointer",
+						"height": "26px",
+						"float" : "left",
+						"line-height": "26px",
+						"padding": "0px 5px",
+						"background-color": "#efefef",
+						"margin" : "5px"
+					}
+				}
+				exportActionNode.setStyles(styles);
+				this.exportActionNode = exportActionNode;
+			}
+
+			if( importenable ){
+				var importActionNode = new Element("div.importActionNode", { text : this.json.importActionText }).inject( importExportAreaNode );
+				if( this.json.importActionStyles ){
+					styles = Object.clone(this.json.importActionStyles);
+				}else{
+					styles = {
+						"color" : "#777",
+						"border-radius": "5px",
+						"border": "1px solid #ccc",
+						"cursor": "pointer",
+						"height": "26px",
+						"float" : "left",
+						"line-height": "26px",
+						"padding": "0px 5px",
+						"background-color": "#efefef",
+						"margin" : "5px"
+					}
+				}
+				importActionNode.setStyles(styles);
+				this.importActionNode = importActionNode;
+			}
+
+			setImportExportAreaNodeWidth();
+		}
+
+		if( name === "impexpPosition" && this.impexpNode && this.importExportAreaNode ){
+			var position = ["leftTop","centerTop","rightTop"].contains( this.json.impexpPosition || "" ) ? "top" : "bottom";
+			this.impexpNode.inject(this.node, position);
+
+			var importExportAreaNode = this.importExportAreaNode;
+			if( ["leftTop","leftBottom"].contains( this.json.impexpPosition || "" ) ){
+				importExportAreaNode.setStyles({ "float" : "left", "margin" : "0px" })
+			}else if( ["rightTop","rightBottom"].contains( this.json.impexpPosition || "" ) ){
+				importExportAreaNode.setStyles({ "float" : "right", "margin" : "0px" })
+			}else{
+				importExportAreaNode.setStyles({ "float" : "none", "margin" : "0px auto" })
+			}
+			setImportExportAreaNodeWidth();
+		}
+
+		if( name === "importActionText" &&  this.importActionNode ){
+			this.importActionNode.set("text", this.json.importActionText );
+			setImportExportAreaNodeWidth();
+		}
+
+		if( name === "exportActionText" &&  this.exportActionNode ){
+			this.exportActionNode.set("text", this.json.exportActionText );
+			setImportExportAreaNodeWidth();
+		}
+
+		if( name === "importActionStyles" &&  this.importActionNode ){
+			this.importActionNode.setStyles( this.json.importActionStyles || {} );
+			setImportExportAreaNodeWidth();
+		}
+
+		if( name === "exportActionStyles" &&  this.exportActionNode ){
+			this.exportActionNode.setStyles( this.json.exportActionStyles || {} );
+			setImportExportAreaNodeWidth();
+		}
+
+
+		//if (name=="sequence") this.checkSequenceShow();
 	},
-    setDatagridStyles: function(){
-        if (this.json.titleStyles){
-            var ths = this.table.getElements("th");
-            ths.each(function(th){
+	setDatagridStyles: function(){
+		if (this.json.titleStyles){
+			var ths = this.table.getElements("th");
+			ths.each(function(th){
 				var opacity = th.getStyle("opacity");
-                this.setCustomNodeStyles(th, this.json.titleStyles);
+				this.setCustomNodeStyles(th, this.json.titleStyles);
 				if(opacity)th.setStyle("opacity", opacity);
-            }.bind(this));
-        }
-        if (this.json.contentStyles){
-            var tds = this.table.getElements("td");
-            tds.each(function(td){
+			}.bind(this));
+		}
+		if (this.json.contentStyles){
+			var tds = this.table.getElements("td");
+			tds.each(function(td){
 				var opacity = td.getStyle("opacity");
-                this.setCustomNodeStyles(td, this.json.contentStyles);
+				this.setCustomNodeStyles(td, this.json.contentStyles);
 				if(opacity)td.setStyle("opacity", opacity);
-            }.bind(this));
-        }
-    },
-    setAllStyles: function(){
-        this.setPropertiesOrStyles("styles");
-        this.setPropertiesOrStyles("tableStyles");
-        this.setPropertiesOrStyles("properties");
-
-        this.setDatagridStyles();
-
-        this.reloadMaplist();
-    },
-
-    //checkSequenceShow: function(){
-    //    if (this.json.sequence=="yes"){
-    //        if (!this.sequenceTitleTd || !this.sequenceTd){
-    //            if (this.sequenceTitleTd){
-    //                this.sequenceTitleTd.destroy();
-    //                this.sequenceTitleTd = null;
-    //            }
-    //            if (this.sequenceTd){
-    //                this.sequenceTd.destroy();
-    //                this.sequenceTd = null;
-    //            }
-    //            var trs = this.node.getElements("tr");
-    //            if (trs[0]){
-    //                this.sequenceTitleTd = new Element("th", {"styles": this.css.sequenceTitleTd}).inject(trs[0], "top");
-    //            }
-    //            if (trs[1]){
-    //                this.sequenceTd = new Element("td", {"styles": this.css.sequenceTd, "text": "1"}).inject(trs[1], "top");
-    //            }
-    //        }
-    //    }else{
-    //        if (this.sequenceTitleTd){
-    //            this.sequenceTitleTd.destroy();
-    //            this.sequenceTitleTd = null;
-    //        }
-    //        if (this.sequenceTd){
-    //            this.sequenceTd.destroy();
-    //            this.sequenceTd = null;
-    //        }
-    //    }
-    //},
+			}.bind(this));
+		}
+	},
+	setAllStyles: function(){
+		this.setPropertiesOrStyles("styles");
+		this.setPropertiesOrStyles("tableStyles");
+		this.setPropertiesOrStyles("properties");
+
+		this.setDatagridStyles();
+
+		this.reloadMaplist();
+	},
+
+	//checkSequenceShow: function(){
+	//    if (this.json.sequence=="yes"){
+	//        if (!this.sequenceTitleTd || !this.sequenceTd){
+	//            if (this.sequenceTitleTd){
+	//                this.sequenceTitleTd.destroy();
+	//                this.sequenceTitleTd = null;
+	//            }
+	//            if (this.sequenceTd){
+	//                this.sequenceTd.destroy();
+	//                this.sequenceTd = null;
+	//            }
+	//            var trs = this.node.getElements("tr");
+	//            if (trs[0]){
+	//                this.sequenceTitleTd = new Element("th", {"styles": this.css.sequenceTitleTd}).inject(trs[0], "top");
+	//            }
+	//            if (trs[1]){
+	//                this.sequenceTd = new Element("td", {"styles": this.css.sequenceTd, "text": "1"}).inject(trs[1], "top");
+	//            }
+	//        }
+	//    }else{
+	//        if (this.sequenceTitleTd){
+	//            this.sequenceTitleTd.destroy();
+	//            this.sequenceTitleTd = null;
+	//        }
+	//        if (this.sequenceTd){
+	//            this.sequenceTd.destroy();
+	//            this.sequenceTd = null;
+	//        }
+	//    }
+	//},
 	getContainerNodes: function(){
 		return this.node.getElements("td");
 	},
-    copyComponentJsonData: function(newNode, pid){
-        var tds = newNode.getElements("td");
-        var ths = newNode.getElements("th");
-        tds.each(function(td, idx){
-            var newContainerJson = Object.clone(this.containers[idx].json);
-            newContainerJson.id = this.containers[idx]._getNewId(pid);
-            this.form.json.moduleList[newContainerJson.id] = newContainerJson;
-            td.set("id", newContainerJson.id);
-        }.bind(this));
-        ths.each(function(th, idx){
-            var newElementJson = Object.clone(this.elements[idx].json);
-            newElementJson.id = this.elements[idx]._getNewId(pid);
-            this.form.json.moduleList[newElementJson.id] = newElementJson;
-            th.set("id", newElementJson.id);
-        }.bind(this));
-    }
-	
+	copyComponentJsonData: function(newNode, pid){
+		var tds = newNode.getElements("td");
+		var ths = newNode.getElements("th");
+		tds.each(function(td, idx){
+			var newContainerJson = Object.clone(this.containers[idx].json);
+			newContainerJson.id = this.containers[idx]._getNewId(pid);
+			this.form.json.moduleList[newContainerJson.id] = newContainerJson;
+			td.set("id", newContainerJson.id);
+		}.bind(this));
+		ths.each(function(th, idx){
+			var newElementJson = Object.clone(this.elements[idx].json);
+			newElementJson.id = this.elements[idx]._getNewId(pid);
+			this.form.json.moduleList[newElementJson.id] = newElementJson;
+			th.set("id", newElementJson.id);
+		}.bind(this));
+	}
+
 });

+ 51 - 0
o2web/source/x_component_process_FormDesigner/Module/Datagrid/datagrid.html

@@ -60,6 +60,57 @@
         </table>
         <div class="MWFScriptArea" name="editableScript" title="是否可编辑(S)"></div>
 
+        <div style="height:24px; text-align: center; line-height: 24px; background-color: #EEE; border-top: 1px solid #999;">导出导入</div>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" class="editTable">
+            <tr>
+                <td class="editTableTitle">启用:</td>
+                <td class="editTableValue">
+                    <input type="radio" name="impexpType" value="none" text{(!$.impexpType || $.impexpType==='none')?'checked':''}
+                           onclick="if (this.checked){ $('text{$.pid}impexpArea').setStyle('display', 'none')}" />禁用
+                    <input type="radio" name="impexpType" value="impexp" text{($.impexpType==='impexp')?'checked':''}
+                           onclick="if (this.checked){ $('text{$.pid}impexpArea').setStyle('display', 'block')}" />导出导入
+                    <input type="radio" name="impexpType" value="exp" text{($.impexpType==='exp')?'checked':''}
+                           onclick="if (this.checked){ $('text{$.pid}impexpArea').setStyle('display', 'block')}" />仅导出
+                    <input type="radio" name="impexpType" value="imp" text{($.impexpType==='imp')?'checked':''}
+                           onclick="if (this.checked){ $('text{$.pid}impexpArea').setStyle('display', 'block')}" />仅导入
+                </td>
+            </tr>
+        </table>
+        <div id="text{$.pid}impexpArea" style="display: text{($.impexpType && $.impexpType!=='none')?'block':'none'};">
+            <table width="100%" border="0" cellpadding="5" cellspacing="0" class="editTable">
+                <tr>
+                    <td class="editTableTitle"></td>
+                    <td class="editTableValue">
+                        注:序号、图片、附件和隐藏列不能导入导出。
+                    </td>
+                </tr>
+                <tr>
+                    <td class="editTableTitle">按钮位置:</td>
+                    <td class="editTableValue">
+                        <input type="radio" name="impexpPosition" value="leftTop" text{(!$.impexpPosition || $.impexpPosition==='leftTop')?'checked':''}/>左上
+                        <input type="radio" name="impexpPosition" value="centerTop" text{($.impexpPosition==='centerTop')?'checked':''}/>中上
+                        <input type="radio" name="impexpPosition" value="rightTop" text{($.impexpPosition==='rightTop')?'checked':''}/>右上
+                        <input type="radio" name="impexpPosition" value="leftBottom" text{($.impexpPosition==='leftBottom')?'checked':''}/>左下
+                        <input type="radio" name="impexpPosition" value="centerBottom" text{($.impexpPosition==='centerBottom')?'checked':''}/>中下
+                        <input type="radio" name="impexpPosition" value="rightBottom" text{($.impexpPosition==='rightBottom')?'checked':''}/>右下
+                    </td>
+                </tr>
+                <tr>
+                    <td class="editTableTitle">导入按钮文本:</td>
+                    <td class="editTableValue"><input type="text" name="importActionText" value="text{$.importActionText || '导入Excel'}" class="editTableInput"/></td>
+                </tr>
+            </table>
+            <div class="MWFMaplist" name="importActionStyles" collapse="true" title="导入按钮样式"></div>
+            <table width="100%" border="0" cellpadding="5" cellspacing="0" class="editTable">
+                <tr>
+                    <td class="editTableTitle">导出按钮文本:</td>
+                    <td class="editTableValue"><input type="text" name="exportActionText" value="text{$.exportActionText || '导出Excel'}" class="editTableInput"/></td>
+                </tr>
+            </table>
+            <div class="MWFMaplist" name="exportActionStyles" collapse="true" title="导出按钮样式"></div>
+            <div class="MWFFormulaArea" name="excelName" title="导出文件名称"></div>
+        </div>
+
         <div class="MWFValidation" name="validationConfig"></div>
         <div class="MWFScriptArea" name="validation" title="校验脚本 (S)"></div>
     </div>

+ 10 - 0
o2web/source/x_component_process_FormDesigner/Module/Datagrid/template.json

@@ -6,6 +6,8 @@
     "sequence": "yes",
     "section": "no",
     "sectionBy": "person",
+	"importActionText" : "导入Excel",
+	"exportActionText" : "导出Excel",
     "sectionByScript": {
       "code": "",
       "html": ""
@@ -51,6 +53,14 @@
 		  "code": "",
 		  "html": ""
 		},
+		"export": {
+			"code": "",
+			"html": ""
+		},
+		"import": {
+			"code": "",
+			"html": ""
+		},
 		"click": {
 			"code": "",
 			"html": ""

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_blue-flat.json

@@ -269,6 +269,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #DCDFE6"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "border": "0",
       "cellpadding": "3",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_blue-simple.json

@@ -244,6 +244,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "border": "0",
       "cellpadding": "3",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_cmcc.json

@@ -252,6 +252,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "border": "0",
       "cellpadding": "3",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_default.json

@@ -249,6 +249,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "width": "100%",
       "border": "0",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_defaultMobile.json

@@ -232,6 +232,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "border": "0",
       "cellpadding": "5",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_mobile-blue-flat.json

@@ -302,6 +302,30 @@
       "margin-bottom" : "10px",
       "margin-top" : "10px"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "width": "100%",
       "border": "0",

+ 2 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_none.json

@@ -126,6 +126,8 @@
     "editStyles": {},
     "amountStyles": {},
     "itemTitleStyles": {},
+    "importActionStyles" : {},
+    "exportActionStyles" : {},
     "properties": {}
   },
   "datagrid$Title": {

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_official.json

@@ -264,6 +264,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "width": "90%",
       "border": "0",

+ 24 - 0
o2web/source/x_component_process_FormDesigner/Module/Form/skin/styles_red-simple.json

@@ -244,6 +244,30 @@
       "font-weight": "bold",
       "border-bottom": "0px solid #cccccc"
     },
+    "importActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
+    "exportActionStyles" : {
+      "color" : "#777",
+      "border-radius": "5px",
+      "border": "1px solid #ccc",
+      "cursor": "pointer",
+      "height": "26px",
+      "float" : "left",
+      "line-height": "26px",
+      "padding": "0px 5px",
+      "background-color": "#efefef",
+      "margin" : "5px"
+    },
     "properties": {
       "width": "90%",
       "border": "0",

+ 6 - 0
o2web/source/x_component_process_FormDesigner/lp/zh-cn.js

@@ -140,6 +140,12 @@ MWF.xApplication.process.FormDesigner.LP = {
             "delete_title": "删除校验内容确认",
             "delete_text": "您确定要删除此项校验码?"
         },
+
+    "datagrid" : {
+		"import" : "导入",
+       "export" : "导出"
+    },
+
     "selectIcon": "选择图标",
     "selectImage": "选择图片",
     "selectApplication" : "选择应用",

+ 27 - 0
o2web/source/x_component_process_Xform/$Form/default/css.wcss

@@ -426,6 +426,33 @@
         "border-radius": "5px",
         "text-align": "center"
     },
+    "gridImpExpAreaNode" : {
+      "display" : "block"
+    },
+  "gridImportActionStyles" : {
+    "color" : "#777",
+    "border-radius": "5px",
+    "border": "1px solid #ccc",
+    "cursor": "pointer",
+    "height": "26px",
+    "float" : "left",
+    "line-height": "26px",
+    "padding": "0px 5px",
+    "background-color": "#efefef",
+    "margin" : "5px"
+  },
+  "gridExportActionStyles" : {
+    "color" : "#777",
+    "border-radius": "5px",
+    "border": "1px solid #ccc",
+    "cursor": "pointer",
+    "height": "26px",
+    "float" : "left",
+    "line-height": "26px",
+    "padding": "0px 5px",
+    "background-color": "#efefef",
+    "margin" : "5px"
+  },
     "mobileGridHelpNode": {
         "width": "30px",
         "height": "30px",

+ 1 - 1
o2web/source/x_component_process_Xform/Attachment.js

@@ -1710,7 +1710,7 @@ MWF.xApplication.process.Xform.Attachment = MWF.APPAttachment = new Class(
      */
     setData: function(data){
         this.attachmentController.clear();
-        data.each(function (att) {
+        ( data || [] ).each(function (att) {
             var attachment = this.form.businessData.attachmentList.find(function(a){
                 return a.id==att.id;
             });

+ 32 - 6
o2web/source/x_component_process_Xform/Checkbox.js

@@ -108,12 +108,15 @@ MWF.xApplication.process.Xform.Checkbox = MWF.APPCheckbox =  new Class(
         this.node.empty();
         this.setOptions();
     },
-    /**
-     * @summary 获取选择项数组.
-     * @example
-     * var array = this.form.get('fieldId').getOptions();
-     * @return {Array} 选择项数组,如果配置为脚本返回计算结果.
-     */
+        /**
+         * @summary 获取选择项。
+         * @return {Array} 返回选择项数组,如果使用选择项脚本,根据脚本返回决定,如:<pre><code class='language-js'>[
+         *  "女|female",
+         *  "男|male"
+         * ]</code></pre>
+         * @example
+         * this.form.get('fieldId').getOptions();
+         */
 	getOptions: function(){
 		if (this.json.itemType == "values"){
 			return this.json.itemValues;
@@ -123,6 +126,29 @@ MWF.xApplication.process.Xform.Checkbox = MWF.APPCheckbox =  new Class(
 		//return [];
 	},
 
+    /**
+     * @summary 获取整理后的选择项。
+     * @return {Object} 返回整理后的选择项,如:
+     * <pre><code class='language-js'>{"valueList": ["","female","male"], "textList": ["","女","男"]}
+     * </code></pre>
+     * @example
+     * var optionData = this.form.get('fieldId').getOptionsObj();
+     */
+    getOptionsObj : function(){
+        var textList = [];
+        var valueList = [];
+        var optionItems = this.getOptions();
+        if (!optionItems) optionItems = [];
+        if (o2.typeOf(optionItems)==="array"){
+            optionItems.each(function(item){
+                var tmps = item.split("|");
+                textList.push( tmps[0] );
+                valueList.push( tmps[1] || tmps[0] );
+            }.bind(this));
+        }
+        return { textList : textList, valueList : valueList };
+    },
+
     setOptions: function(){
         var optionItems = this.getOptions();
         this._setOptions(optionItems);

+ 3 - 1
o2web/source/x_component_process_Xform/DatagridMobile.js

@@ -66,7 +66,9 @@ MWF.xApplication.process.Xform.DatagridMobile = new Class(
 
 
         this.editable = (!this.readonly);
-        if (this.editable) this.editable = this.form.Macro.exec(((this.json.editableScript) ? this.json.editableScript.code : ""), this);
+        if (this.editable && this.json.editableScript && this.json.editableScript.code){
+            this.editable = this.form.Macro.exec(((this.json.editableScript) ? this.json.editableScript.code : ""), this);
+        }
         //this.editable = false;
 
         this.deleteable = this.json.deleteable !== "no";

Разница между файлами не показана из-за своего большого размера
+ 2188 - 1374
o2web/source/x_component_process_Xform/DatagridPC.js


+ 20 - 8
o2web/source/x_component_process_Xform/Log.js

@@ -185,25 +185,31 @@ MWF.xApplication.process.Xform.Log = MWF.APPLog =  new Class(
                         if (this.isNumber(value1) && this.isNumber(value2)) {
                             if (parseFloat(value1) !== parseFloat(value2)) {
                                 return parseFloat(value1) - parseFloat(value2);
+                            }else{
+                                if( this.json.sortTypeInCategory === "none" ){
+                                    return 0;
+                                }else{
+                                    return Date.parse(a.recordTime) - Date.parse(b.recordTime);
+                                }
                             }
                         } else if (!this.isNumber(value1) && !this.isNumber(value2)) {
                             if( this.json.sortTypeInCategory === "none" ){
-                                return -1;
+                                return 0;
                             }else{
-                                return Date.parse(a.fromTime) - Date.parse(b.fromTime);
+                                return Date.parse(a.recordTime) - Date.parse(b.recordTime);
                             }
                         } else {
                             return this.isNumber(value1) ? -1 : 1;
                         }
                     }
-                    return Date.parse(a.fromTime) - Date.parse(b.fromTime);
+                    return Date.parse(a.recordTime) - Date.parse(b.recordTime);
                 }else if( a.properties.fromOpinionGroup || b.properties.fromOpinionGroup ){
                     return a.properties.fromOpinionGroup ? -1 : 1;
                 }else{
                     if( this.json.sortTypeInCategory === "none" ){
-                        return -1;
+                        return 0;
                     }else{
-                        return Date.parse(a.fromTime) - Date.parse(b.fromTime);
+                        return Date.parse(a.recordTime) - Date.parse(b.recordTime);
                     }
                 }
             }.bind(this))
@@ -800,10 +806,16 @@ MWF.xApplication.process.Xform.Log = MWF.APPLog =  new Class(
                       if (this.isNumber(value1) && this.isNumber(value2)) {
                           if (parseFloat(value1) !== parseFloat(value2)) {
                               return parseFloat(value1) - parseFloat(value2);
-                          }
+                          }else{
+                                if( this.json.sortTypeInCategory === "none" ){
+                                    return 0;
+                                }else{
+                                    return Date.parse(a.fromTime) - Date.parse(b.fromTime);
+                                }
+                            }
                       } else if (!this.isNumber(value1) && !this.isNumber(value2)) {
                           if( this.json.sortTypeInCategory === "none" ){
-                              return -1;
+                              return 0;
                           }else{
                               return Date.parse(a.fromTime) - Date.parse(b.fromTime);
                           }
@@ -816,7 +828,7 @@ MWF.xApplication.process.Xform.Log = MWF.APPLog =  new Class(
                   return a.fromOpinionGroup ? -1 : 1;
               }else{
                   if( this.json.sortTypeInCategory === "none" ){
-                      return -1;
+                      return 0;
                   }else{
                       return Date.parse(a.fromTime) - Date.parse(b.fromTime);
                   }

+ 34 - 6
o2web/source/x_component_process_Xform/Radio.js

@@ -126,12 +126,15 @@ MWF.xApplication.process.Xform.Radio = MWF.APPRadio =  new Class(
         this.node.empty();
         this.setOptions();
     },
-    /**
-     * @summary 获取选择项。
-     * @return {Array} 返回选择项数组,如果使用选择项脚本,根据脚本返回决定
-     * @example
-     * this.form.get('fieldId').getOptions();
-     */
+        /**
+         * @summary 获取选择项。
+         * @return {Array} 返回选择项数组,如果使用选择项脚本,根据脚本返回决定,如:<pre><code class='language-js'>[
+         *  "女|female",
+         *  "男|male"
+         * ]</code></pre>
+         * @example
+         * this.form.get('fieldId').getOptions();
+         */
 	getOptions: function(){
 		if (this.json.itemType == "values"){
 			return this.json.itemValues;
@@ -140,6 +143,30 @@ MWF.xApplication.process.Xform.Radio = MWF.APPRadio =  new Class(
 		}
 		return [];
 	},
+
+    /**
+     * @summary 获取整理后的选择项。
+     * @return {Object} 返回整理后的选择项,如:
+     * <pre><code class='language-js'>{"valueList": ["","female","male"], "textList": ["","女","男"]}
+     * </code></pre>
+     * @example
+     * var optionData = this.form.get('fieldId').getOptionsObj();
+     */
+    getOptionsObj : function(){
+        var textList = [];
+        var valueList = [];
+        var optionItems = this.getOptions();
+        if (!optionItems) optionItems = [];
+        if (o2.typeOf(optionItems)==="array"){
+            optionItems.each(function(item){
+                var tmps = item.split("|");
+                textList.push( tmps[0] );
+                valueList.push( tmps[1] || tmps[0] );
+            }.bind(this));
+        }
+        return { textList : textList, valueList : valueList };
+    },
+
     setOptions: function(){
         var optionItems = this.getOptions();
         this._setOptions(optionItems);
@@ -362,6 +389,7 @@ MWF.xApplication.process.Xform.Radio = MWF.APPRadio =  new Class(
     resetData: function(){
         this.setData(this.getValue());
     },
+
     /**
      * @summary 获取选中的Dom对象。
      * @return {Element} 返回选中的Dom对象

+ 28 - 23
o2web/source/x_component_process_Xform/Select.js

@@ -158,7 +158,10 @@ MWF.xApplication.process.Xform.Select = MWF.APPSelect =  new Class(
     },
 	/**
 	 * @summary 获取选择项。
-	 * @return {Array} 返回选择项数组,如果使用选择项脚本,根据脚本返回决定
+	 * @return {Array} 返回选择项数组,如果使用选择项脚本,根据脚本返回决定,如:<pre><code class='language-js'>[
+	 *  "女|female",
+	 *  "男|male"
+	 * ]</code></pre>
 	 * @example
 	 * this.form.get('fieldId').getOptions();
 	 */
@@ -170,6 +173,30 @@ MWF.xApplication.process.Xform.Select = MWF.APPSelect =  new Class(
 		}
 		return [];
 	},
+
+	/**
+	 * @summary 获取整理后的选择项。
+	 * @return {Object} 返回整理后的选择项,如:
+	 * <pre><code class='language-js'>{"valueList": ["","female","male"], "textList": ["","女","男"]}
+	 * </code></pre>
+	 * @example
+	 * var optionData = this.form.get('fieldId').getOptionsObj();
+	 */
+	getOptionsObj : function(){
+		var textList = [];
+		var valueList = [];
+		var optionItems = this.getOptions();
+		if (!optionItems) optionItems = [];
+		if (o2.typeOf(optionItems)==="array"){
+			optionItems.each(function(item){
+				var tmps = item.split("|");
+				textList.push( tmps[0] );
+				valueList.push( tmps[1] || tmps[0] );
+			}.bind(this));
+		}
+		return { textList : textList, valueList : valueList };
+	},
+
 	setOptions: function(){
 		var optionItems = this.getOptions();
 		this._setOptions(optionItems);
@@ -389,28 +416,6 @@ MWF.xApplication.process.Xform.Select = MWF.APPSelect =  new Class(
 
         this.setData(this.getValue());
     },
-	/**
-	 * @summary 获取整理后的选择项。
-	 * @return {Object} 返回整理后的选择项,如:
-	 * <pre><code class='language-js'>{"value": ["","female","male"], "text": ["","女","男"]}
-	 * </code></pre>
-	 * @example
-	 * var optionData = this.form.get('fieldId').getOptionsObj();
-	 */
-	getOptionsObj : function(){
-		var textList = [];
-		var valueList = [];
-		var optionItems = this.getOptions();
-		if (!optionItems) optionItems = [];
-		if (o2.typeOf(optionItems)==="array"){
-			optionItems.each(function(item){
-				var tmps = item.split("|");
-				textList.push( tmps[0] );
-				valueList.push( tmps[1] || tmps[0] );
-			}.bind(this));
-		}
-		return { textList : textList, valueList : valueList };
-	},
 
 	setData: function(data){
 		return this._setValue(data, "__setData");

+ 14 - 0
o2web/source/x_component_process_Xform/lp/zh-cn.js

@@ -8,6 +8,20 @@ MWF.xApplication.process.Xform.LP = {
     "cancelDatagridLineEditTitle" : "取消编辑数据网格确认",
     "cancelDatagridLineEdit" : "是否确定取消编辑数据网格?",
 
+    "datagridImport" : "导入Excel",
+    "datagridExport" : "导出Excel",
+    "exportDefaultName" : "数据网格",
+    "importSuccess" : "导入成功!",
+    "validationInfor" : "校验信息",
+    "importFail" : "导入失败",
+    "importValidationColumnText" : "第{n}列:",
+    "importValidationColumnTextExcel" : "列{n}:",
+    "notValidNumber" : "不是数字",
+    "notValidDate" : "不是日期格式",
+    "fullstop" : "。",
+    "notExistInSystem" : "在系统中不存在",
+    "uploadedFilesCannotHaveSpaces" : "上传的文件不能带空格",
+
     "browserNotActiveX": "您的浏览器不支持ActiveX控件,不能加载Office控件,请使用IE浏览器",
 
     "activity": "活动",

+ 760 - 6
o2web/source/x_desktop/js/o2m.api.js

@@ -25,12 +25,26 @@
  *
  * ***** END LICENSE BLOCK ******/
 
+
+
+/**
+ * <b>o2m</b> 是O2OA移动端APP提供的调用原生控件的能力,帮助开发者高效使用拍照、定位等手机系统的能力,同时可以直接使用扫一扫、打开原生应用、选择时间,人员,组织等业务的能力,带给门户接近原生代码的体验
+ * <br/>
+ * <b>o2m</b> 只有在O2OA移动端APP中才能提供能力
+ * @module o2m
+ * @o2ordernumber 160
+ * @o2range {流程表单|门户}
+ * @o2syntax
+ * // 可以在移动端 流程表单、门户 上使用
+ * this.o2m
+ * 
+ */
 (function () {
   this.o2m = {
     version: {
-      v: "1.0.0",
-      build: "2019.04.20",
-      info: "O2OA 活力办公 创意无限. Copyright © 2019, o2oa.net O2 Team All rights reserved."
+      v: "1.1.0",
+      build: "2021.01.20",
+      info: "O2OA 活力办公 创意无限. Copyright © 2021, o2oa.net O2 Team All rights reserved."
     },
     log: function (message) {
       window.o2android && window.o2android.o2mLog ? window.o2android.o2mLog(message) : window.webkit.messageHandlers.o2mLog.postMessage(message);
@@ -69,7 +83,7 @@
     }
   };
 
-  // notification.alert
+
   this.o2m.notification.alertSuccess = function () {
     console.log("notification alert back");
   };
@@ -97,6 +111,33 @@
     };
     _notification_post(body, onFail);
   };
+  /**
+   * 原生Alert提示弹出窗
+   * @method alert
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj 提示窗传入对象
+   * <pre><code class='language-js'>{
+   *  "title": "提示",  //消息标题,可为空
+   *  "message": "这里是消息内容", //消息内容
+   *  "buttonName": "确定", //确定按钮名称
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/notification_alert.jpeg">
+   * </caption>
+   * o2m.notification.alert({
+   *  message: "亲爱的",
+   *  title: "提示",//可传空
+   *  buttonName: "收到",
+   *  onSuccess : function() {//onSuccess将在点击button之后回调},
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.alert = _o2m_n_alert;
 
   //notification.confirm
@@ -131,6 +172,36 @@
     };
     _notification_post(body, onFail);
   }
+  /**
+   * 原生confirm提示弹出窗
+   * @method confirm
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj 提示窗传入对象
+   * <pre><code class='language-js'>{
+   *  "title": "提示",  //消息标题,可为空
+   *  "message": "这里是消息内容", //消息内容
+   *  "buttonLabels": ["确定", "取消"], //按钮名称 Array[String]
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/notification_confirm.jpeg">
+   * </caption>
+   * o2m.notification.confirm({
+   *  message: "你爱我吗",
+   *  title: "提示",//可传空
+   *  buttonLabels: ['爱', '不爱'],
+   *  onSuccess : function(buttonIndex) {
+   *    //onSuccess将在点击button之后回调
+   *    //buttonIndex: 0 被点击按钮的索引值,Number类型,从0开始
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.confirm = _o2m_n_confirm;
 
   //notification.prompt
@@ -165,6 +236,39 @@
     };
     _notification_post(body, onFail);
   }
+  /**
+   * 原生prompt提示弹出窗
+   * @method prompt
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj prompt需要传入对象
+   * <pre><code class='language-js'>{
+   *  "title": "提示",  //消息标题,可为空
+   *  "message": "这里是消息内容", //消息内容
+   *  "buttonLabels": ['继续', '不玩了'], //按钮名称 Array[String]
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/notification_prompt.jpeg">
+   * </caption>
+   * o2m.notification.prompt({
+   *  message: "再说一遍?",
+   *  title: "提示",//可传空
+   *  buttonLabels: ['继续', '不玩了'],
+   *  onSuccess : function(result) {
+   *     //result是一个字符串,格式是json格式,内容如下:
+   *     //{
+   *     //    buttonIndex: 0, //被点击按钮的索引值,Number类型,从0开始
+   *     //    value: '' //输入的值
+   *     //}
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.prompt = _o2m_n_prompt;
 
 
@@ -189,6 +293,27 @@
     };
     _notification_post(body, onFail);
   };
+
+  /**
+   * 手机震动
+   * @method vibrate
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj 震动需要传入对象
+   * <pre><code class='language-js'>{
+   *  "duration": 300,  //震动时间,android可配置 iOS忽略
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.notification.vibrate({
+   *  duration: 300, 
+   *  onSuccess : function() {
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.vibrate = _o2m_n_vibrate;
 
   //notification.toast
@@ -217,6 +342,27 @@
     };
     _notification_post(body, onFail);
   };
+
+  /**
+   * toast提示
+   * @method toast
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj toast需要传入对象
+   * <pre><code class='language-js'>{
+   *  "message": "这里是提示信息",
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.notification.toast({
+   *  message: "提示消息内容", 
+   *  onSuccess : function() {
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.toast = _o2m_n_toast;
 
   //notification.actionSheet
@@ -251,6 +397,35 @@
     };
     _notification_post(body, onFail);
   };
+  /**
+   * 底部弹出菜单
+   * @method actionSheet
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj actionSheet需要传入对象
+   * <pre><code class='language-js'>{
+   *  "title": "谁是最棒哒?",//标题
+   *  "cancelButton": '取消', //取消按钮文本
+   *  "otherButtons": ["孙悟空","猪八戒","唐僧","沙和尚"], //其他选项按钮名称
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/notification_action_sheet.jpeg">
+   * </caption>
+   * o2m.notification.actionSheet({
+   *  title: "谁是最棒哒?", //标题
+   *  cancelButton: '取消', //取消按钮文本
+   *  otherButtons: ["孙悟空","猪八戒","唐僧","沙和尚"],
+   *  onSuccess : function(buttonIndex) {
+   *  //buttonIndex: 0 被点击按钮的索引值,Number,从0开始, 取消按钮为-1
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.actionSheet = _o2m_n_actionSheet;
 
   //notification.showLoading
@@ -273,6 +448,30 @@
     };
     _notification_post(body, onFail);
   };
+  /**
+   * 显示Loading浮层,请和hideLoading配合使用
+   * @method showLoading
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj showLoading需要传入对象
+   * <pre><code class='language-js'>{
+   *  text: "使劲加载中..", //loading显示的字符,空表示不显示文字
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/notification_showLoading.jpeg">
+   * </caption>
+   * o2m.notification.showLoading({
+   *  text: "使劲加载中..", //loading显示的字符,空表示不显示文字
+   *  onSuccess : function() {
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.showLoading = _o2m_n_showLoading;
 
   //notification.hideLoading
@@ -293,6 +492,24 @@
     };
     _notification_post(body, onFail);
   }
+  /**
+   * 隐藏Loading浮层
+   * @method hideLoading
+   * @o2membercategory notification
+   * @static
+   * @param {Object} obj hideLoading需要传入对象
+   * <pre><code class='language-js'>{
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.notification.hideLoading({
+   *  onSuccess : function() {
+   * },
+   *  onFail : function(err) {}
+   * });
+   * 
+   */
   this.o2m.notification.hideLoading = _o2m_n_hideLoading;
 
 
@@ -366,6 +583,34 @@
     };
     _util_post(body, onFail);
   };
+   
+  /**
+   * 日期选择器
+   * @method datePicker
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  datePicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  value: '2019-04-17', //默认显示日期
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_date_picker.jpeg">
+   * </caption>
+   * o2m.util.date.datePicker({
+   * value: '2019-04-17', //默认显示日期
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         value: "2019-02-10"
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.date.datePicker = _o2m_u_date_datePicker;
 
   //o2m.util.date.timePicker
@@ -388,6 +633,33 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 时间选择器
+   * @method timePicker
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  timePicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  value: '14:00', //默认显示时间
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_time_picker.jpeg">
+   * </caption>
+   * o2m.util.date.timePicker({
+   * value: '14:00', //默认显示时间
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         value: "18:10"
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.date.timePicker = _o2m_u_date_timePicker;
 
 
@@ -412,6 +684,33 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 日期时间选择器
+   * @method dateTimePicker
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  dateTimePicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  value: '2019-05-05 14:00', //默认显示时间
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_date_time_picker.jpeg">
+   * </caption>
+   * o2m.util.date.dateTimePicker({
+   * value: '2019-05-05 14:00', //默认显示时间
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         value: "2019-03-18 18:10"
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.date.dateTimePicker = _o2m_u_date_dateTimePicker;
 
 
@@ -436,6 +735,33 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 月历日期选择器
+   * @method chooseOneDay
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  chooseOneDay需要传入对象
+   * <pre><code class='language-js'>{
+   *  value: '2019-05-05', //默认显示日期
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_choose_one_day.jpeg">
+   * </caption>
+   * o2m.util.date.chooseOneDay({
+   * value: '2019-05-05', //默认显示日期
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         value: "2019-03-18"
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.calendar.chooseOneDay = _o2m_u_calendar_chooseOneDay;
 
 
@@ -459,6 +785,33 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 月历日期时间选择器
+   * @method chooseDateTime
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  chooseDateTime需要传入对象
+   * <pre><code class='language-js'>{
+   *  value: '2019-05-05 11:00', //默认显示时间
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_choose_date_time.jpeg">
+   * </caption>
+   * o2m.util.date.chooseDateTime({
+   * value: '2019-05-05 11:00', //默认显示时间
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         value: "2019-03-18 18:45"
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.calendar.chooseDateTime = _o2m_u_calendar_chooseDateTime;
 
 
@@ -484,6 +837,35 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 月历日期区间选择器
+   * @method chooseInterval
+   * @o2membercategory util.date
+   * @static
+   * @param {Object} obj  chooseInterval需要传入对象
+   * <pre><code class='language-js'>{
+   *  startDate: '2019-05-05',
+   *  endDate: '2019-05-06',
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/util_choose_interval.jpeg">
+   * </caption>
+   * o2m.util.date.chooseInterval({
+   * value: '2019-05-05 11:00', //默认显示时间
+   * onSuccess : function(result) {
+   *     //onSuccess将在点击完成之后回调
+   *     {
+   *         startDate: "2019-05-05", 
+            endDate: "2019-05-06",
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.calendar.chooseInterval = _o2m_u_calendar_chooseInterval;
 
 
@@ -506,6 +888,32 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 获取手机基础信息
+   * @method getPhoneInfo
+   * @o2membercategory util.device
+   * @static
+   * @param {Object} obj  getPhoneInfo需要传入对象
+   * <pre><code class='language-js'>{
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.util.device.getPhoneInfo({
+   * onSuccess : function(result) {
+   *     {
+   *      screenWidth: 1080, // 手机屏幕宽度
+   *      screenHeight: 1920, // 手机屏幕高度
+   *      brand:'Mi', // 手机品牌
+   *      model:'Note4', // 手机型号
+   *      version:'7.0'. // 版本
+   *      netInfo:'wifi' , // 网络类型 wifi/4g/3g 
+   *      operatorType :'xx' // 运营商信息
+   *     }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.device.getPhoneInfo = _o2m_u_device_getPhoneInfo;
 
 
@@ -528,14 +936,32 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 扫二维码
+   * @method scan
+   * @o2membercategory util.device
+   * @static
+   * @param {Object} obj  scan需要传入对象
+   * <pre><code class='language-js'>{
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.util.device.scan({
+   * onSuccess : function(result) {
+   *     { 'text': '扫码内容'}
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.device.scan = _o2m_u_device_scan;
 
-  
+
   //o2m.util.device.location
   this.o2m.util.device.locationSuccess = function (result) {
     console.log("util device location back, result:" + result);
   };
-  var _o2m_u_device_location = function(c) {
+  var _o2m_u_device_location = function (c) {
     var onSuccess = c && c.onSuccess ? c.onSuccess : null;
     var onFail = c && c.onFail ? c.onFail : null;
     if (onSuccess && typeof onSuccess === "function") {
@@ -550,6 +976,29 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 单次定位
+   * @method location
+   * @o2membercategory util.device
+   * @static
+   * @param {Object} obj  location需要传入对象
+   * <pre><code class='language-js'>{
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.util.device.location({
+   * onSuccess : function(result) {
+   *     //这里返回百度坐标系的定位信息 
+   *     { 
+   *      'latitude': 39.903578, // 纬度
+   *      'longitude': 116.473565, // 经度
+   *      'address': '地址描述'
+   *      }
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.device.location = _o2m_u_device_location;
 
 
@@ -573,6 +1022,25 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 设置原生页面标题
+   * @method setTitle
+   * @o2membercategory util.navigation
+   * @static
+   * @param {Object} obj  setTitle需要传入对象
+   * <pre><code class='language-js'>{
+   *  title : '导航标题',    
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * o2m.util.navigation.setTitle({
+   * title : '导航标题', 
+   * onSuccess : function() {
+   *  },
+   *  onFail : function(err) {}
+   *});
+   */
   this.o2m.util.navigation.setTitle = _o2m_u_navigation_setTitle;
 
 
@@ -594,6 +1062,14 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 关闭当前原生页面
+   * @method close
+   * @o2membercategory util.navigation
+   * @static
+   * @example
+   * o2m.util.navigation.close();
+   */
   this.o2m.util.navigation.close = _o2m_u_navigation_close;
 
 
@@ -615,6 +1091,14 @@
     };
     _util_post(body, onFail);
   };
+  /**
+   * 返回上级原生页面
+   * @method goBack
+   * @o2membercategory util.navigation
+   * @static
+   * @example
+   * o2m.util.navigation.goBack();
+   */
   this.o2m.util.navigation.goBack = _o2m_u_navigation_goBack;
 
 
@@ -662,6 +1146,14 @@
       console.log("请在O2OA移动端使用, workClose");
     }
   }
+  /**
+   * 关闭当前工作页面,<b>只能在工作表单中可以使用</b>
+   * @method workClose
+   * @o2membercategory biz
+   * @static
+   * @example
+   * o2m.biz.workClose();
+   */
   this.o2m.biz.workClose = _o2m_b_work_close;
 
 
@@ -693,6 +1185,51 @@
     };
     _biz_post(body, onFail);
   };
+  /**
+   * 通讯录选部门
+   * @method departmentsPicker
+   * @o2membercategory biz
+   * @static
+   * @param {Object} obj  departmentsPicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  topList: [],//Array[String] 可选的顶级组织列表,不传或列表为空的时候,显示全部组织
+   *  orgType: "",//String 可选择的组织类别。为空就是全部组织类型都可以
+   *  multiple:true, //是否多选
+   *  maxNumber: 0, //Int 当multiple为true的时候,最多可选择的部门数
+   *  pickedDepartments:[],//Array[String] 已经选择的部门distinguishedName列表
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/biz_dept_choose.png">
+   * </caption>
+   * o2m.biz.contact.departmentsPicker({
+   * topList: [],//不传或者空列表,显示全部组织
+   * orgType: "",//可传空 只显示某种类型的组织
+   * multiple:true, //是否多选
+   * maxNumber: 0, //最大选择数量
+   * pickedDepartments:[],//已选部门
+   * onSuccess : function(result) {
+   *     //返回结果样例
+   *     {
+   *       departments:[{
+   *       "id":"xxxx",
+   *       "name":"部门名称", 
+   *       "unique":"xxxx",
+   *       "distinguishedName":"部门@xxxx@U",
+   *       "typeList":["xxxx"],
+   *       "shortName":"xxxx",
+   *       "level": 0,
+   *       "levelName":"xxxx",
+   *       }]
+   *     }
+   *   
+   * },
+   * onFail : function(err) {}
+   * });
+   */
   this.o2m.biz.contact.departmentsPicker = _o2m_b_contact_department_picker;
 
 
@@ -724,6 +1261,55 @@
     };
     _biz_post(body, onFail);
   };
+  /**
+   * 通讯录选身份
+   * @method IdentityPicker
+   * @o2membercategory biz
+   * @static
+   * @param {Object} obj  IdentityPicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  topList: [],//Array[String] 可选的顶级组织列表,不传或列表为空的时候,显示全部组织
+   *  multiple:true, //Boolean 是否多选
+   *  maxNumber: 0, //Int 当multiple为true的时候,最多可选择的身份数
+   *  pickedIdentities:[],//Array[String] 已经选择的身份distinguishedName列表
+   *  duty: [],//Array[String] 可选择的人员职责
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/biz_identity_choose.png">
+   * </caption>
+   * o2m.biz.contact.IdentityPicker({
+   * topList: [],//不传或者空列表,显示全部组织
+   * multiple:true, //是否多选
+   * maxNumber: 0, //最大选择数量
+   * pickedIdentities:[],//已选身份列表
+   * duty: [],//人员职责
+   * onSuccess : function(result) {
+   *     //返回结果样例
+   *     {
+   *       identities:[{
+   *         "id":"xxxx",
+   *         "name":"姓名", 
+   *         "distinguishedName":"姓名@xxxx@I",
+   *         "person":"xxx", 
+   *         "unique":"xxxx",
+   *         "unit":"xxx",
+   *         "unitName":"xxxx",
+   *         "unitLevel": 0,
+   *         "unitLevelName":"xxxx",
+   *         "personName":"xxxx",
+   *         "personUnique":"xxx",
+   *         "personDn":"xxx"
+   *         }]
+   *     }
+   *   
+   * },
+   * onFail : function(err) {}
+   * });
+   */
   this.o2m.biz.contact.IdentityPicker = _o2m_b_contact_identity_picker;
 
 
@@ -752,6 +1338,43 @@
     };
     _biz_post(body, onFail);
   };
+  /**
+   * 群组选择
+   * @method GroupPicker
+   * @o2membercategory biz
+   * @static
+   * @param {Object} obj  GroupPicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  multiple:true, //Boolean 是否多选
+   *  maxNumber: 0, //Int 当multiple为true的时候,最多可选择的身份数
+   *  pickedGroups:[],//Array[String] 已经选择的群组distinguishedName列表
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/biz_group_choose.png">
+   * </caption>
+   * o2m.biz.contact.GroupPicker({
+   * multiple:true, //是否多选
+   * maxNumber: 0, //最大选择数量
+   * pickedGroups:[],//已选群组列表
+   * onSuccess : function(result) {
+   *     //返回结果样例
+   *     {
+   *        groups:[{
+   *           "id":"xxxx", 
+   *           "name":"群组名称", 
+   *           "distinguishedName":"群组名称@xxxx@G"
+   *           "unique":"xxxx", 
+   *           }]
+   *     }
+   *   
+   * },
+   * onFail : function(err) {}
+   * });
+   */
   this.o2m.biz.contact.GroupPicker = _o2m_b_contact_group_picker;
 
 
@@ -779,6 +1402,50 @@
     };
     _biz_post(body, onFail);
   };
+  /**
+   * 人员选择
+   * @method PersonPicker
+   * @o2membercategory biz
+   * @static
+   * @param {Object} obj  PersonPicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  multiple:true, //Boolean 是否多选
+   *  maxNumber: 0, //Int 当multiple为true的时候,最多可选择的身份数
+   *  pickedUsers:[],//Array[String] 已经选择的人员distinguishedName列表
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/biz_person_choose.png">
+   * </caption>
+   * o2m.biz.contact.PersonPicker({
+   * multiple:true, //是否多选
+   * maxNumber: 0, //最大选择数量
+   * pickedUsers:[],//已选人员列表
+   * onSuccess : function(result) {
+   *     //返回结果样例
+   *     {
+   *         users:[{
+   *           "id":"xxx", 
+   *           "name":"姓名", 
+   *           "unique":"xxx", 
+   *           "distinguishedName":"姓名@xxxx@P"
+   *           "genderType":"xxx", 
+   *           "employee":"xxx", 
+   *           "mail":"xxx", 
+   *           "weixin":"xxx", 
+   *           "qq":"xxx", 
+   *           "mobile":"xxx", 
+   *           "officePhone":"xxx"
+   *           }]
+   *     }
+   *   
+   * },
+   * onFail : function(err) {}
+   * });
+   */
   this.o2m.biz.contact.PersonPicker = _o2m_b_contact_person_picker;
 
 
@@ -820,6 +1487,93 @@
     };
     _biz_post(body, onFail);
   };
+  /**
+   * 复合选择器,可配置选择多种数据
+   * @method ComplexPicker
+   * @o2membercategory biz
+   * @static
+   * @param {Object} obj  ComplexPicker需要传入对象
+   * <pre><code class='language-js'>{
+   *  pickMode: ["departmentPicker", "identityPicker"], //Array[String] 选择器类型,可传入值:departmentPicker、identityPicker、groupPicker、personPicker
+   *  topList:Array[String] 可选的顶级组织列表,不传或列表为空的时候,显示全部组织
+   *  duty: Array[String] 可选择的人员职责
+   *  orgType:String 可选择的组织类别。为空就是全部组织类型都可以
+   *  multiple:true, //Boolean 是否多选
+   *  maxNumber: 0, //Int 当multiple为true的时候,最多可选择的身份数
+   *  pickedDepartments:Array[String] 已经选择的部门distinguishedName列表
+   *  pickedIdentities:Array[String] 已经选择的身份distinguishedName列表
+   *  pickedGroups: Array[String] 已经选择的群组distinguishedName列表
+   *  pickedUsers:[],//Array[String] 已经选择的人员distinguishedName列表
+   *  "onSuccess": function,  //成功回调
+   *  "onFail": function, //失败回调
+   * }</code></pre>
+   * @example
+   * <caption>
+   * 样例效果:<br/>
+   * <img src="img/module/o2m/biz_complex_choose.png">
+   * </caption>
+   * o2m.biz.contact.ComplexPicker({
+   * pickMode: ["departmentPicker", "identityPicker"], //选择器类型
+   * topList: [],//不传或者空列表,显示全部组织
+   * orgType: "",//可传空 只显示某种类型的组织
+   * duty: [],//人员职责
+   * multiple:true, //是否多选
+   * maxNumber: 0, //最大选择数量
+   * pickedDepartments:[],//已选部门
+   * pickedIdentities:[],//已选身份列表
+   * pickedGroups:[],//已选群组列表
+   * pickedUsers:[],//已选人员列表
+   * onSuccess : function(result) {
+   *     //返回结果样例
+   *     {
+   *       departments:[{
+   *       "id":"xxxx",
+   *       "name":"部门名称", 
+   *       "unique":"xxxx",
+   *       "distinguishedName":"部门@xxxx@U",
+   *       "typeList":["xxxx"],
+   *       "shortName":"xxxx",
+   *       "level": 0,
+   *       "levelName":"xxxx",
+   *       }],
+   *       identities:[{
+   *       "id":"xxxx",
+   *       "name":"姓名", 
+   *       "distinguishedName":"姓名@xxxx@I",
+   *       "person":"xxx", 
+   *       "unique":"xxxx",
+   *       "unit":"xxx",
+   *       "unitName":"xxxx",
+   *       "unitLevel": 0,
+   *       "unitLevelName":"xxxx",
+   *       "personName":"xxxx",
+   *       "personUnique":"xxx",
+   *       "personDn":"xxx"
+   *       }],
+   *       groups:[{
+   *       "id":"xxxx", 
+   *       "name":"群组名称", 
+   *       "distinguishedName":"群组名称@xxxx@G"
+   *       "unique":"xxxx", 
+   *       }],
+   *       users:[{
+   *       "id":"xxx", 
+   *       "name":"姓名", 
+   *       "unique":"xxx", 
+   *       "distinguishedName":"姓名@xxxx@P"
+   *       "genderType":"xxx", 
+   *       "employee":"xxx", 
+   *       "mail":"xxx", 
+   *       "weixin":"xxx", 
+   *       "qq":"xxx", 
+   *       "mobile":"xxx", 
+   *       "officePhone":"xxx"
+   *       }]
+   *     }
+   * },
+   * onFail : function(err) {}
+   * });
+   */
   this.o2m.biz.contact.ComplexPicker = _o2m_b_contact_complex_picker;
 
 

+ 1 - 0
o2web/source/x_desktop/portalmobile.html

@@ -24,6 +24,7 @@
         <script src="../o2_core/o2.min.js"></script>
         <script src="../o2_lib/Decimal.js"></script>
         <script src="js/base_portal.min.js"></script>
+        <script src="js/o2m.api.min.js"></script>
         <script src="js/portal.min.js"></script>
         <script>layout.mobile = true;</script>
     </body>

+ 1 - 1
o2web/source/x_desktop/sso.html

@@ -44,7 +44,7 @@
                             }.bind(this)
                         });
                         res.setHeader("Content-Type", "application/json; charset=utf-8");
-                        var json = {"token": xtoken, "client": client};
+                        var json = {"token": encodeURIComponent(xtoken), "client": client};
 
                         res.send(JSON.encode(json));
                     }else{

+ 1 - 1
o2web/source/x_desktop/workmobilewithaction.html

@@ -32,7 +32,7 @@
 
     <script src="../o2_core/o2.min.js"></script>
     <script src="js/base_work.min.js"></script>
-    <script src="js/o2m.api.js"></script>
+    <script src="js/o2m.api.min.js"></script>
     <script src="js/work.min.js"></script>
     <script src="../o2_lib/Decimal.js"></script>
     <script>layout.mobile = true;</script>

Некоторые файлы не были показаны из-за большого количества измененных файлов