소스 검색

新增客户端版本更新功能

hou 5 일 전
부모
커밋
90b24f8242
35개의 변경된 파일12102개의 추가작업 그리고 275개의 파일을 삭제
  1. 1004 0
      _gen_inventory.py
  2. 9703 0
      _migrate_inventory.md
  3. 2 0
      db/mysql/js_sys_log_json_result.sql
  4. 14 0
      db/mysql/js_sys_log_type_client.sql
  5. 32 0
      src/main/java/com/jeesite/modules/config/WebConfig.java
  6. 181 0
      src/main/java/com/jeesite/modules/interceptor/MesClientAccessLogInterceptor.java
  7. 9 2
      src/main/java/com/jeesite/modules/mes/dao/MesProductFswDao.java
  8. 9 0
      src/main/java/com/jeesite/modules/mes/entity/MesProductFsw.java
  9. 13 0
      src/main/java/com/jeesite/modules/mes/service/MesProductFswService.java
  10. 20 0
      src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java
  11. 25 0
      src/main/java/com/jeesite/modules/mes/util/CommonUitl.java
  12. 3 3
      src/main/java/com/jeesite/modules/mes/web/MesGlueRecordController.java
  13. 4 14
      src/main/java/com/jeesite/modules/mes/web/MesMaterialReportController.java
  14. 1 1
      src/main/java/com/jeesite/modules/mes/web/MesProcessAlarmController.java
  15. 5 11
      src/main/java/com/jeesite/modules/mes/web/MesProcessCheckRecordController.java
  16. 2 1
      src/main/java/com/jeesite/modules/mes/web/MesProductCncLifeController.java
  17. 29 6
      src/main/java/com/jeesite/modules/mes/web/MesProductFswController.java
  18. 2 1
      src/main/java/com/jeesite/modules/mes/web/MesProductGp12Controller.java
  19. 2 1
      src/main/java/com/jeesite/modules/mes/web/MesProductInsulation2Controller.java
  20. 3 2
      src/main/java/com/jeesite/modules/mes/web/MesProductProdController.java
  21. 28 12
      src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java
  22. 1 1
      src/main/java/com/jeesite/modules/mes/web/MesProductRivetController.java
  23. 81 0
      src/main/java/com/jeesite/modules/sys/log/LogJsonResultAdvice.java
  24. 32 0
      src/main/java/com/jeesite/modules/sys/log/LogJsonResultHolder.java
  25. 245 0
      src/main/java/com/jeesite/modules/sys/log/LogJsonResultSupport.java
  26. 6 6
      src/main/resources/config/application.yml
  27. 16 9
      src/main/resources/mappings/modules/mes/MesProductFswDao.xml
  28. 1 1
      src/main/resources/views/modules/mes/mesMaterialCheckRecordForm2.html
  29. 37 13
      src/main/resources/views/modules/mes/mesProductCmtInfo.html
  30. 63 0
      src/main/resources/views/modules/mes/mesProductFswInfo.html
  31. 16 17
      src/main/resources/views/modules/mes/mesProductFswList.html
  32. 2 0
      src/main/resources/views/modules/mes/mesProductRecordWork.html
  33. 364 0
      src/main/resources/views/modules/sys/logForm.html
  34. 147 0
      src/main/resources/views/modules/sys/logList.html
  35. 0 174
      views/modules/sys/user/empUserFormAuthDataScope.html

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1004 - 0
_gen_inventory.py


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 9703 - 0
_migrate_inventory.md


+ 2 - 0
db/mysql/js_sys_log_json_result.sql

@@ -0,0 +1,2 @@
+-- 访问日志增加返回参数(对齐若依操作日志 json_result)
+ALTER TABLE `js_sys_log` ADD COLUMN `json_result` longtext NULL COMMENT '返回参数';

+ 14 - 0
db/mysql/js_sys_log_type_client.sql

@@ -0,0 +1,14 @@
+-- 访问日志增加「客户端」类型,用于工位/大屏/免登录接口
+INSERT INTO js_sys_dict_data (
+  dict_code, parent_code, parent_codes, tree_sort, tree_sorts, tree_leaf, tree_level, tree_names,
+  dict_label, dict_value, dict_type, is_sys, description, css_style, css_class,
+  status, create_by, create_date, update_by, update_date, corp_code, corp_name
+)
+SELECT
+  REPLACE(UUID(), '-', ''), '0', '0,', 70, '0000000070,', '1', 0, '客户端',
+  '客户端', 'client', 'sys_log_type', '0', '工位/客户端免登录接口', '', 'tag green',
+  '0', 'system', NOW(), 'system', NOW(), '0', 'JeeSite'
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM js_sys_dict_data WHERE dict_type = 'sys_log_type' AND dict_value = 'client'
+);

+ 32 - 0
src/main/java/com/jeesite/modules/config/WebConfig.java

@@ -1,8 +1,13 @@
 package com.jeesite.modules.config;
 
+import com.jeesite.common.config.Global;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.interceptor.MesClientAccessLogInterceptor;
 import com.jeesite.modules.interceptor.SiteClosedInterceptor;
+import com.jeesite.modules.sys.log.LogJsonResultSupport;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -10,9 +15,36 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 public class WebConfig implements WebMvcConfigurer {
     @Autowired
     private SiteClosedInterceptor siteClosedInterceptor;
+    @Autowired
+    private LogJsonResultSupport logJsonResultSupport;
+    @Autowired
+    private MesClientAccessLogInterceptor mesClientAccessLogInterceptor;
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(siteClosedInterceptor);
+        registry.addInterceptor(logJsonResultSupport).addPathPatterns("/**/sys/log/form");
+        applyLogPaths(registry.addInterceptor(mesClientAccessLogInterceptor));
+    }
+
+    private void applyLogPaths(InterceptorRegistration registration) {
+        String add = Global.getProperty("web.interceptor.log.addPathPatterns");
+        String exclude = Global.getProperty("web.interceptor.log.excludePathPatterns");
+        if (StringUtils.isBlank(add)) {
+            registration.addPathPatterns("/a/**");
+        } else {
+            for (String p : StringUtils.split(add, ",")) {
+                if (StringUtils.isNotBlank(p)) {
+                    registration.addPathPatterns(p.trim());
+                }
+            }
+        }
+        if (StringUtils.isNotBlank(exclude)) {
+            for (String p : StringUtils.split(exclude, ",")) {
+                if (StringUtils.isNotBlank(p)) {
+                    registration.excludePathPatterns(p.trim());
+                }
+            }
+        }
     }
 }

+ 181 - 0
src/main/java/com/jeesite/modules/interceptor/MesClientAccessLogInterceptor.java

@@ -0,0 +1,181 @@
+package com.jeesite.modules.interceptor;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import com.jeesite.common.idgen.IdGen;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.sys.entity.User;
+import com.jeesite.modules.sys.utils.LogUtils;
+import com.jeesite.modules.sys.utils.UserUtils;
+
+/**
+ * 记录工位/客户端等免登录接口访问日志(操作人标记为「未登录客户端」)。
+ */
+@Component
+public class MesClientAccessLogInterceptor implements HandlerInterceptor {
+
+	public static final String LOG_TYPE = "client";
+	public static final String USER_CODE = "client";
+	public static final String USER_NAME = "未登录客户端";
+
+	private static final Logger logger = LoggerFactory.getLogger(MesClientAccessLogInterceptor.class);
+	private static final ThreadLocal<Long> START_TIME = new NamedThreadLocal<Long>("MesClientAccessLog StartTime");
+
+	private static final String[][] TITLE_MAP = {
+			{"/mesLogin/login", "客户端登录"},
+			{"/mesApp/ver", "客户端版本"},
+			{"/mesClientUpgrade/checkUpdate", "客户端升级检查"},
+			{"/mesDeviceState/add", "设备状态上报"},
+			{"/mesProductRecord/submit", "过站提交"},
+			{"/mesProductRecord/result", "过站结果"},
+			{"/mesProductRecord/checkDJ", "点检校验"},
+			{"/mesProductRecord/checkQuality", "质量校验"},
+			{"/mesProductRecord/qmcheck", "气密校验"},
+			{"/mesProductRecord/qmresult", "气密结果"},
+			{"/mesProductRecord/cnccheck", "CNC校验"},
+			{"/mesProductRecord/cncresult", "CNC结果"},
+			{"/mesProductRecord/pccheck", "过程校验"},
+			{"/mesProductRecord/pcresult", "过程结果"},
+			{"/mesProductRecord/pccreate", "过程创建"},
+			{"/mesProductRecord/ccdcheck", "CCD校验"},
+			{"/mesProductRecord/check", "过站校验"},
+			{"/mesProductRecord/select", "扫码查询"},
+			{"/mesProductRecord/ghtime", "过站时间"},
+			{"/mesProductRecord/device", "过站设备"},
+			{"/mesProductRecord/process", "过站工序"},
+			{"/mesProductRecord/yamao", "压铆过站"},
+			{"/mesProductRecord/currentProduct", "当前工件"},
+			{"/mesProductRecord/workData", "工位数据"},
+			{"/mesProductRecord/winfo", "工位信息"},
+			{"/mesProductRecord/work", "工位作业"},
+			{"/mesProductRecord/updateQualityByTiming", "定时质量更新"},
+			{"/mesProductRecord/getConfig", "工位配置"},
+			{"/mesProductRecord/updateOprno", "工位切换"},
+			{"/mesProductRecord/queryProduct", "工件查询"},
+			{"/mesProductRecord/changeLine", "换线"},
+			{"/mesProductFsw/batchsave", "FSW批量上传"},
+			{"/mesProductFsw/add", "FSW上报"},
+			{"/mesProductRivet/batchUpload", "压铆上传"},
+			{"/mesProductGumming/add", "涂胶上报"},
+			{"/mesProductGumming/getSn", "涂胶取码"},
+			{"/mesProductCcd/add", "CCD上报"},
+			{"/mesProductCcd/testDate", "CCD检测"},
+			{"/mesProductProd/batchsave", "过程参数上传"},
+			{"/mesProductBs/batchsave", "BS参数上传"},
+			{"/mesProductCmt/batchsave", "CMT参数上传"},
+			{"/mesProductYgsl/batchsave", "压合参数上传"},
+			{"/mesProductHb/upload", "焊机上传"},
+			{"/mesProductInsulation2/batchUpload", "绝缘测试上传"},
+			{"/mesElectricTorque/batchSave", "电枪扭矩上传"},
+			{"/mesProduct/screen", "生产大屏"},
+			{"/mesProduct/alarmData", "大屏报警数据"},
+			{"/mesProcessAlarm/alarmCount", "报警计数"},
+			{"/mesProcessAlarm/add", "报警上报"},
+			{"/mesProcessCheckRecord/addCheck", "过程点检提交"},
+			{"/mesProcessCheckRecord/checkView", "过程点检查看"},
+			{"/mesProcessCheckRecord/listData", "过程点检查询"},
+			{"/mesProcessCheckRecord/check", "过程点检"},
+			{"/mesProcessCheckGz/submit", "工装点检提交"},
+			{"/mesMaterialPrebind/bind", "物料预绑定"},
+			{"/mesLineProcessMaterial/materials", "工位物料"},
+			{"/mesGp12Record/upload", "GP12上传"},
+			{"/mesProductGp12/saveNg", "GP12不合格"},
+			{"/mesProductGp12/listData", "GP12查询"},
+			{"/mesProductGp12/ulist", "GP12列表"},
+			{"/mesProductWsn/createSn", "生成工件码"},
+			{"/mesProductDbjBind/dbjBind", "挡板筋绑定"},
+			{"/mesProductQm/addItem", "气密项上报"},
+			{"/mesGlueRecord/editSave", "涂胶记录保存"},
+			{"/mesGlueRecord/listData2", "涂胶记录查询"},
+			{"/mesGlueRecord/olist", "涂胶记录列表"},
+			{"/mesGlueRecord/edit", "涂胶记录编辑"},
+			{"/mesDeviceTime/listData2", "设备时间查询"},
+			{"/mesDeviceTime/list2", "设备时间"},
+			{"/mesProductPush/test", "推送测试"},
+			{"/mesGp12Items/items", "GP12项目"}
+	};
+
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+	@PostConstruct
+	public void ensureLogTypeDict() {
+		try {
+			Integer cnt = jdbcTemplate.queryForObject(
+					"SELECT COUNT(1) FROM js_sys_dict_data WHERE dict_type = 'sys_log_type' AND dict_value = ?",
+					Integer.class, LOG_TYPE);
+			if (cnt != null && cnt > 0) {
+				return;
+			}
+			String id = IdGen.nextId();
+			jdbcTemplate.update(
+					"INSERT INTO js_sys_dict_data (dict_code, parent_code, parent_codes, tree_sort, tree_sorts, tree_leaf, "
+							+ "tree_level, tree_names, dict_label, dict_value, dict_type, is_sys, description, css_style, css_class, "
+							+ "status, create_by, create_date, update_by, update_date, corp_code, corp_name) "
+							+ "VALUES (?, '0', '0,', 70, '0000000070,', '1', 0, '客户端', '客户端', ?, 'sys_log_type', '0', "
+							+ "'工位/客户端免登录接口', '', 'tag green', '0', 'system', NOW(), 'system', NOW(), '0', 'JeeSite')",
+					id, LOG_TYPE);
+			logger.info("已增加访问日志类型:客户端");
+		} catch (Exception e) {
+			logger.warn("增加客户端日志类型失败: {}", e.getMessage());
+		}
+	}
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+		START_TIME.set(System.currentTimeMillis());
+		return true;
+	}
+
+	@Override
+	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
+		try {
+			User user = UserUtils.getUser();
+			if (user != null && StringUtils.isNotBlank(user.getUserCode())) {
+				return;
+			}
+			long start = 0L;
+			Long startObj = START_TIME.get();
+			if (startObj != null) {
+				start = startObj;
+			}
+			long cost = start > 0 ? (System.currentTimeMillis() - start) : 0L;
+			User client = new User();
+			client.setUserCode(USER_CODE);
+			client.setUserName(USER_NAME);
+			LogUtils.saveLog(client, request, handler, ex, resolveTitle(request), LOG_TYPE, cost);
+		} catch (Exception e) {
+			logger.debug("记录客户端访问日志失败: {}", e.getMessage());
+		} finally {
+			START_TIME.remove();
+		}
+	}
+
+	private String resolveTitle(HttpServletRequest request) {
+		String uri = request.getRequestURI();
+		if (uri == null) {
+			uri = "";
+		}
+		for (int i = 0; i < TITLE_MAP.length; i++) {
+			if (uri.contains(TITLE_MAP[i][0])) {
+				return "【客户端】" + TITLE_MAP[i][1];
+			}
+		}
+		String shortUri = uri;
+		int mes = uri.lastIndexOf("/mes/");
+		if (mes >= 0) {
+			shortUri = uri.substring(mes);
+		}
+		return "【客户端】" + shortUri;
+	}
+}

+ 9 - 2
src/main/java/com/jeesite/modules/mes/dao/MesProductFswDao.java

@@ -4,6 +4,8 @@ import com.jeesite.common.dao.CrudDao;
 import com.jeesite.common.mybatis.annotation.MyBatisDao;
 import com.jeesite.modules.mes.entity.MesProductFsw;
 
+import java.util.List;
+
 /**
  * FSW过程参数DAO接口
  * @author hzd
@@ -11,5 +13,10 @@ import com.jeesite.modules.mes.entity.MesProductFsw;
  */
 @MyBatisDao
 public interface MesProductFswDao extends CrudDao<MesProductFsw> {
-	
-}
+
+	/**
+	 * 按工件码+产线分组查询(分页由框架拦截器处理)
+	 */
+	List<MesProductFsw> findGroupList(MesProductFsw mesProductFsw);
+
+}

+ 9 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProductFsw.java

@@ -35,6 +35,7 @@ public class MesProductFsw extends DataEntity<MesProductFsw> {
 	private String spindleSpeed;		// 主轴转速
 	private String feedRate;		// 进给速度
 	private String pressure;		// 焊接压力
+	private Long recordCount;		// 同一工件采样记录数(分组查询)
 
 	public MesProductFsw() {
 		this(null);
@@ -96,6 +97,14 @@ public class MesProductFsw extends DataEntity<MesProductFsw> {
 	public void setPressure(String pressure) {
 		this.pressure = pressure;
 	}
+
+	public Long getRecordCount() {
+		return recordCount;
+	}
+
+	public void setRecordCount(Long recordCount) {
+		this.recordCount = recordCount;
+	}
 	
 	public Date getCreateDate_gte() {
 		return sqlMap.getWhere().getValue("create_date", QueryType.GTE);

+ 13 - 0
src/main/java/com/jeesite/modules/mes/service/MesProductFswService.java

@@ -37,6 +37,19 @@ public class MesProductFswService extends CrudService<MesProductFswDao, MesProdu
 	public Page<MesProductFsw> findPage(MesProductFsw mesProductFsw) {
 		return super.findPage(mesProductFsw);
 	}
+
+	/**
+	 * 按工件码+产线分组分页(分页 LIMIT 由 MyBatis 拦截器追加)
+	 */
+	public Page<MesProductFsw> findGroupPage(MesProductFsw mesProductFsw) {
+		Page<MesProductFsw> page = mesProductFsw.getPage();
+		if (page == null) {
+			page = new Page<>();
+			mesProductFsw.setPage(page);
+		}
+		page.setList(dao.findGroupList(mesProductFsw));
+		return page;
+	}
 	
 	/**
 	 * 查询列表数据

+ 20 - 0
src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java

@@ -704,6 +704,26 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 					mesProductModifyRecord1.setCreateBy(userCode);
 					mesProductModifyRecord1.setUpdateBy(userCode);
 					mesProductModifyRecordService.save(mesProductModifyRecord1);
+				}else if(mesProductRecord.getCraft().equals("400001") && StringUtils.isNotEmpty(userCode)){
+					if(oldOprno.length() == 6){
+						mesProductRecord.setOprno(oldOprno);
+					}
+					mesProductRecord.setMaterielSn(userCode);
+					mesProductRecord.setContent("OK");
+					super.update(mesProductRecord);
+
+					MesProductModifyRecord mesProductModifyRecord1 = new MesProductModifyRecord();
+					mesProductModifyRecord1.setOprno(oldOprno);
+					mesProductModifyRecord1.setSn(mesProductRecord.getSn());
+					mesProductModifyRecord1.setCraft(mesProductRecord.getCraft());
+					mesProductModifyRecord1.setLineSn(mesProductRecord.getLineSn());
+					mesProductModifyRecord1.setContent("OK");
+					mesProductModifyRecord1.setContentOld(oldcontent);
+					mesProductModifyRecord1.setSource("0");
+					mesProductModifyRecord1.setMaterielSn(userCode);
+					mesProductModifyRecord1.setCreateBy(userCode);
+					mesProductModifyRecord1.setUpdateBy(userCode);
+					mesProductModifyRecordService.save(mesProductModifyRecord1);
 				}
 
 				// 更新主结果

+ 25 - 0
src/main/java/com/jeesite/modules/mes/util/CommonUitl.java

@@ -603,4 +603,29 @@ public class CommonUitl {
         return list;
     }
 
+    /**
+     * 当前登录用户编号(带 __sid 或 Cookie 时可用)。
+     */
+    public static String currentUserCode() {
+        try {
+            com.jeesite.modules.sys.entity.User user = com.jeesite.modules.sys.utils.UserUtils.getUser();
+            if (user != null && StringUtils.isNotBlank(user.getUserCode())) {
+                return user.getUserCode();
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return "";
+    }
+
+    /**
+     * 优先用请求里的 ucode,没有则用当前登录用户。
+     */
+    public static String resolveUserCode(String ucode) {
+        if (StringUtils.isNotBlank(ucode)) {
+            return ucode.trim();
+        }
+        return currentUserCode();
+    }
+
 }

+ 3 - 3
src/main/java/com/jeesite/modules/mes/web/MesGlueRecordController.java

@@ -95,7 +95,7 @@ public class MesGlueRecordController extends BaseController {
 	@RequestMapping(value = {"olist", ""})
 	public String olist(MesGlueRecord mesGlueRecord, Model model, HttpServletRequest request) {
 		String oprno = request.getParameter("oprno");
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String lineSn = request.getParameter("lineSn");
 		model.addAttribute("mesGlueRecord", mesGlueRecord);
 		model.addAttribute("oprno", oprno);
@@ -115,7 +115,7 @@ public class MesGlueRecordController extends BaseController {
 
 	@RequestMapping(value = "edit")
 	public String edit(MesGlueRecord mesGlueRecord, Model model, HttpServletRequest request) {
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		model.addAttribute("ucode", ucode);
 		model.addAttribute("mesGlueRecord", mesGlueRecord);
 		String oriOprno = CommonUitl.formatOprno(mesGlueRecord.getOprno()); // 原始工位,不带ABCD
@@ -138,7 +138,7 @@ public class MesGlueRecordController extends BaseController {
 		if(StringUtils.isEmpty(mesGlueRecord.getOprno())||StringUtils.isEmpty(mesGlueRecord.getLineSn())){
 			return renderResult(Global.TRUE, text("参数错误!"));
 		}
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 
 		String oriOprno = CommonUitl.formatOprno(mesGlueRecord.getOprno()); // 原始工位,不带ABCD
 

+ 4 - 14
src/main/java/com/jeesite/modules/mes/web/MesMaterialReportController.java

@@ -1,6 +1,5 @@
 package com.jeesite.modules.mes.web;
 
-import java.text.ParseException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -14,7 +13,6 @@ import com.jeesite.modules.mes.entity.MesMaterial;
 import com.jeesite.modules.mes.entity.MesProductRecord;
 import com.jeesite.modules.mes.resp.CommonResp;
 import com.jeesite.modules.mes.service.MesMaterialService;
-import com.jeesite.modules.utils.DateLocalUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -385,19 +383,11 @@ public class MesMaterialReportController extends BaseController {
 
 		String materialId = matcher.group(1);      // 料号
 		String yyMMdd = dateSerial.substring(0, 6); // 日期部分 260601
-		String faDateStr = "20" + yyMMdd.substring(0, 2) + "-"
+		// 直接拼 yyyy-MM-dd;勿再经 getCurrentDate2(其按 yyyyMMdd 解析,
+		// 传入带横线的日期会在宽松模式下错成如 2025-12-07)
+		String faDate1 = "20" + yyMMdd.substring(0, 2) + "-"
 				+ yyMMdd.substring(2, 4) + "-"
-				+ yyMMdd.substring(4, 6);           // 转换为 yyyy-MM-dd
-
-		// 2. 日期格式转换(沿用原有工具)
-		String faDate1;
-		try {
-			faDate1 = DateLocalUtils.getCurrentDate2(faDateStr);
-		} catch (ParseException e) {
-			resp.setResult(Global.FALSE);
-			resp.setMessage("二维码日期解析失败!");
-			return resp;
-		}
+				+ yyMMdd.substring(4, 6);
 
 		// 3. 检查物料是否存在
 		MesMaterial mesMaterial = new MesMaterial();

+ 1 - 1
src/main/java/com/jeesite/modules/mes/web/MesProcessAlarmController.java

@@ -109,7 +109,7 @@ public class MesProcessAlarmController extends BaseController {
 	@PostMapping(value = "add")
 	@ResponseBody
 	public String add(@Validated MesProcessAlarm mesProcessAlarm, HttpServletRequest request) {
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		mesProcessAlarm.setCreateBy(ucode);
 		mesProcessAlarm.setUpdateBy(ucode);
 		mesProcessAlarm.setRemark("设备报警");

+ 5 - 11
src/main/java/com/jeesite/modules/mes/web/MesProcessCheckRecordController.java

@@ -22,8 +22,6 @@ import com.jeesite.modules.mes.resp.ParamsResp;
 import com.jeesite.modules.mes.service.*;
 import com.jeesite.modules.mes.util.CommonUitl;
 import com.jeesite.modules.mes.util.CopyUtil;
-import com.jeesite.modules.sys.entity.User;
-import com.jeesite.modules.sys.utils.UserUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.beetl.ext.fn.Json;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -81,14 +79,10 @@ public class MesProcessCheckRecordController extends BaseController {
 	@RequestMapping(value = {"ulist", ""})
 	public String ulist(MesProcessCheckRecord mesProcessCheckRecord, Model model, HttpServletRequest request, HttpServletResponse response) {
 
-		String ucode = request.getParameter("ucode");
-		if(StringUtils.isEmpty(ucode)){
-			String sid = request.getParameter("__sid");
-			if(!StringUtils.isEmpty(sid)){
-				CookieUtils.setCookie(response,"jeesite.session.id",request.getParameter("__sid"));
-				User user = UserUtils.getUser();
-				ucode = user.getUserCode();
-			}
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
+		String sid = request.getParameter("__sid");
+		if(!StringUtils.isEmpty(sid)){
+			CookieUtils.setCookie(response,"jeesite.session.id",sid);
 		}
 		model.addAttribute("ucode", ucode);
 
@@ -175,7 +169,7 @@ public class MesProcessCheckRecordController extends BaseController {
 		List<MesProcessCheck> list = mesProcessCheckService.findList(mesProcessCheck);
 
 		logger.info("checks:"+ JSON.toJSONString(list));
-		String ucode = req.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(req.getParameter("ucode"));
 		model.addAttribute("ucode", ucode);
 
 		model.addAttribute("list", list);

+ 2 - 1
src/main/java/com/jeesite/modules/mes/web/MesProductCncLifeController.java

@@ -28,6 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductCncLife;
 import com.jeesite.modules.mes.service.MesProductCncLifeService;
+import com.jeesite.modules.mes.util.CommonUitl;
 
 /**
  * CNC刀具寿命Controller
@@ -140,7 +141,7 @@ public class MesProductCncLifeController extends BaseController {
 		String sn = request.getParameter("sn");
 		String lineSn = request.getParameter("lineSn");
 		String oprno = request.getParameter("oprno");
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String params = request.getParameter("params");
 		if(StringUtils.isEmpty(sn)||StringUtils.isEmpty(lineSn)||StringUtils.isEmpty(oprno)||StringUtils.isEmpty(ucode)||StringUtils.isEmpty(params)) {
 			return renderResult(Global.FALSE, text("参数错误!"));

+ 29 - 6
src/main/java/com/jeesite/modules/mes/web/MesProductFswController.java

@@ -61,15 +61,38 @@ public class MesProductFswController extends BaseController {
 	}
 	
 	/**
-	 * 查询列表数据
+	 * 查询列表数据(按工件码+产线整合)
 	 */
 	@RequiresPermissions("mes:mesProductFsw:view")
 	@RequestMapping(value = "listData")
 	@ResponseBody
 	public Page<MesProductFsw> listData(MesProductFsw mesProductFsw, HttpServletRequest request, HttpServletResponse response) {
 		mesProductFsw.setPage(new Page<>(request, response));
-		Page<MesProductFsw> page = mesProductFswService.findPage(mesProductFsw);
-		return page;
+		return mesProductFswService.findGroupPage(mesProductFsw);
+	}
+
+	/**
+	 * 同一工件参数详情页
+	 */
+	@RequiresPermissions("mes:mesProductFsw:view")
+	@RequestMapping(value = "info")
+	public String info(MesProductFsw mesProductFsw, Model model) {
+		model.addAttribute("mesProductFsw", mesProductFsw);
+		model.addAttribute("detailList", findDetailList(mesProductFsw));
+		return "modules/mes/mesProductFswInfo";
+	}
+
+	private List<MesProductFsw> findDetailList(MesProductFsw mesProductFsw) {
+		if (StringUtils.isEmpty(mesProductFsw.getSn())) {
+			return java.util.Collections.emptyList();
+		}
+		MesProductFsw query = new MesProductFsw();
+		query.setSn(mesProductFsw.getSn());
+		if (StringUtils.isNotEmpty(mesProductFsw.getLineSn())) {
+			query.setLineSn(mesProductFsw.getLineSn());
+		}
+		query.getSqlMap().getOrder().setOrderBy("a.create_date ASC");
+		return mesProductFswService.findList(query);
 	}
 
 	/**
@@ -104,7 +127,7 @@ public class MesProductFswController extends BaseController {
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
 		String sn = request.getParameter("sn");
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String feedRate = request.getParameter("feedRate");
 		String pressure = request.getParameter("pressure");
 		String spindleSpeed = request.getParameter("spindleSpeed");
@@ -119,7 +142,7 @@ public class MesProductFswController extends BaseController {
 		mesProductFsw.setSn(sn);
 		mesProductFsw.setOprno(oprno);
 		mesProductFsw.setLineSn(lineSn);
-		mesProductFsw.setCreateBy(ucode);
+		mesProductFsw.setCreateBy(StringUtils.isEmpty(ucode) ? "system" : ucode);
 		mesProductFsw.setFeedRate(feedRate);
 		mesProductFsw.setPressure(pressure);
 		mesProductFsw.setSpindleSpeed(spindleSpeed);
@@ -153,7 +176,7 @@ public class MesProductFswController extends BaseController {
 			String feedRate = obj.getString("feedRate");
 			String pressure = obj.getString("pressure");
 			String spindleSpeed = obj.getString("spindleSpeed");
-			String ucode = obj.getString("ucode");
+			String ucode = CommonUitl.resolveUserCode(obj.getString("ucode"));
 
 			if (StringUtils.isEmpty(feedRate) || StringUtils.isEmpty(pressure)
 					|| StringUtils.isEmpty(spindleSpeed) || StringUtils.isEmpty(oprno)

+ 2 - 1
src/main/java/com/jeesite/modules/mes/web/MesProductGp12Controller.java

@@ -11,6 +11,7 @@ import com.jeesite.modules.file.entity.FileUpload;
 import com.jeesite.modules.file.service.FileUploadService;
 import com.jeesite.modules.file.utils.FileUploadUtils;
 import com.jeesite.modules.mes.resp.CommonResp;
+import com.jeesite.modules.mes.util.CommonUitl;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -97,7 +98,7 @@ public class MesProductGp12Controller extends BaseController {
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
 		String remark = request.getParameter("remark");
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		if (StringUtils.isEmpty(sn) || StringUtils.isEmpty(oprno) || StringUtils.isEmpty(lineSn)) {
 			resp.setResult(Global.FALSE);
 			resp.setMessage("参数错误");

+ 2 - 1
src/main/java/com/jeesite/modules/mes/web/MesProductInsulation2Controller.java

@@ -25,6 +25,7 @@ import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductInsulation2;
 import com.jeesite.modules.mes.service.MesLogService;
 import com.jeesite.modules.mes.service.MesProductInsulation2Service;
+import com.jeesite.modules.mes.util.CommonUitl;
 
 /**
  * 绝缘耐压检测Controller
@@ -130,7 +131,7 @@ public class MesProductInsulation2Controller extends BaseController {
 
 		String sn = request.getParameter("sn");
 		String steps = request.getParameter("steps");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 
 		if (StringUtils.isEmpty(sn) || StringUtils.isEmpty(steps)) {
 			return renderResult(Global.FALSE, text("参数错误!"));

+ 3 - 2
src/main/java/com/jeesite/modules/mes/web/MesProductProdController.java

@@ -152,7 +152,7 @@ public class MesProductProdController extends BaseController {
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
 		String sn = request.getParameter("sn");
-		String ucode = request.getParameter("ucode");
+		String ucode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		if(StringUtils.isEmpty(params) || StringUtils.isEmpty(oprno) || StringUtils.isEmpty(lineSn)){
 			return renderResult(Global.FALSE, text("参数错误!"));
 		}
@@ -170,7 +170,8 @@ public class MesProductProdController extends BaseController {
 			mesProductFsw.setSn(obj.getString("sn"));
 			mesProductFsw.setOprno(obj.getString("gw"));
 			mesProductFsw.setLineSn(obj.getString("lineSn"));
-			mesProductFsw.setCreateBy(obj.getString("ucode"));
+			String createBy = CommonUitl.resolveUserCode(obj.getString("ucode"));
+			mesProductFsw.setCreateBy(StringUtils.isEmpty(createBy) ? ucode : createBy);
 			mesProductFsw.setType(obj.getString("type"));
 			mesProductFsw.setDateTime(CommonUitl.String2DateTime(obj.getString("recordTime")));
 			mesProductFsw.setFmax(obj.getString("fmax"));

+ 28 - 12
src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java

@@ -919,7 +919,7 @@ public class MesProductRecordController extends BaseController {
 		String sn = request.getParameter("sn").trim();
 		String oprno = request.getParameter("oprno").trim();
 		String lineSn = request.getParameter("lineSn").trim();
-		String user = request.getParameter("ucode").trim();
+		String user = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String wsn = request.getParameter("wsn").trim();
 		String craft = request.getParameter("craft").trim();
 		mesLogService.apisave2(oprno,lineSn,JSON.toJSONString(request.getParameterMap()),request);
@@ -945,7 +945,7 @@ public class MesProductRecordController extends BaseController {
 		String sn = request.getParameter("sn").trim();
 		String oprno = request.getParameter("oprno").trim();
 		String lineSn = request.getParameter("lineSn").trim();
-		String user = request.getParameter("ucode").trim();
+		String user = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String wsn = request.getParameter("wsn").trim();
 		String craft = request.getParameter("craft").trim();
 		mesLogService.apisave2(oprno,lineSn,JSON.toJSONString(request.getParameterMap()),request);
@@ -1076,7 +1076,7 @@ public class MesProductRecordController extends BaseController {
 		CommonResp<String> resp = new CommonResp<>();
 		String sn = request.getParameter("sn");
 		String lineSn = request.getParameter("lineSn");
-		String userCode = request.getParameter("userCode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("userCode"));
 		String oprno = request.getParameter("oprno");
 
 		if(StringUtils.isEmpty(sn) || StringUtils.isEmpty(oprno) || StringUtils.isEmpty(lineSn)){
@@ -2506,7 +2506,7 @@ public class MesProductRecordController extends BaseController {
 			return resp;
 		}
 
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 
 		String ret = checkQualityCommon(oldOprno,sn,lineSn,userCode);
 		String checkret = ret.substring(3,5);
@@ -2532,7 +2532,7 @@ public class MesProductRecordController extends BaseController {
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
 		String result = request.getParameter("result");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String craft = "100000";
 
 		String ps = request.getParameter("ps"); // 参数以"|"分割
@@ -2848,6 +2848,7 @@ public class MesProductRecordController extends BaseController {
 		Map map = mesPlanService.getCurShiftPlan(mesProductRecord.getOprno(),mesProductRecord.getLineSn(),"");
 		logger.info("map:"+JSON.toJSONString(map));
 		model.addAttribute("map", map);
+		model.addAttribute("showQmColumns", isQmStation(mesProductRecord.getOprno(), mesProductRecord.getLineSn()));
 
 		return "modules/mes/mesProductRecordWork";
 	}
@@ -2868,7 +2869,8 @@ public class MesProductRecordController extends BaseController {
 		mesProductRecord.getSqlMap().getOrder().setOrderBy("a.update_date DESC");
 
 		Page<MesProductRecord> page = mesProductRecordService.findPage(mesProductRecord);
-		if (page != null && page.getList() != null) {
+		if (page != null && page.getList() != null
+				&& isQmStation(mesProductRecord.getOprno(), mesProductRecord.getLineSn())) {
 			for (MesProductRecord record : page.getList()) {
 				fillQmWorkFields(record);
 			}
@@ -2876,6 +2878,20 @@ public class MesProductRecordController extends BaseController {
 		return page;
 	}
 
+	/** 产线工序勾选了气密工位时,工作记录才展示测试压/泄漏值 */
+	private boolean isQmStation(String oprno, String lineSn) {
+		if (StringUtils.isEmpty(oprno) || StringUtils.isEmpty(lineSn)) {
+			return false;
+		}
+		try {
+			MesLineProcess process = mesLineProcessService.findInfoByOprnoLineSn(oprno, lineSn);
+			return process != null && "1".equals(process.getIsQm());
+		} catch (Exception e) {
+			logger.info("判断气密工位失败 oprno={}, lineSn={}, err={}", oprno, lineSn, e.getMessage());
+			return false;
+		}
+	}
+
 	/** 工作记录展示:从 mes_product_qm 补齐测试压、泄漏值 */
 	private void fillQmWorkFields(MesProductRecord record) {
 		if (record == null || StringUtils.isEmpty(record.getSn()) || StringUtils.isEmpty(record.getOprno())) {
@@ -3167,7 +3183,7 @@ public class MesProductRecordController extends BaseController {
 		mesLogService.apisave2(oprno,lineSn,JSON.toJSONString(mesProductProdVisual),request);
 
 
-		String userCode = "";
+		String userCode = CommonUitl.currentUserCode();
 		List<ParamsResp> params = new ArrayList<>();
 		try{
 			// 2.判断物料批次是否符合绑定
@@ -3285,7 +3301,7 @@ public class MesProductRecordController extends BaseController {
 		mesLogService.apisave2(oprno,lineSn,JSON.toJSONString(mesTorsionalProcess),request);
 
 
-		String userCode = "";
+		String userCode = CommonUitl.currentUserCode();
 		List<ParamsResp> params = new ArrayList<>();
 		try{
 			// 2.判断物料批次是否符合绑定
@@ -3348,7 +3364,7 @@ private MesProductProdVisual2Service mesProductProdVisual2Service;
 		mesLogService.apisave2(oprno,lineSn,JSON.toJSONString(mesProductProdVisual2),request);
 
 
-		String userCode = "";
+		String userCode = CommonUitl.currentUserCode();
 		List<ParamsResp> params = new ArrayList<>();
 		try{
 			// 2.判断物料批次是否符合绑定
@@ -3836,7 +3852,7 @@ private MesProductProdVisual2Service mesProductProdVisual2Service;
 		String sn = request.getParameter("sn");
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 
 		// 格式化工件码(带子工位)
 		String oldOprno = oprno;
@@ -3884,7 +3900,7 @@ private MesProductProdVisual2Service mesProductProdVisual2Service;
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
 		String result = request.getParameter("result");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String craft = request.getParameter("craft");
 		if (StringUtils.isEmpty(craft)){
 			craft = "100000";
@@ -3950,7 +3966,7 @@ private MesProductProdVisual2Service mesProductProdVisual2Service;
 		String sn = request.getParameter("sn");
 		String oprno = request.getParameter("oprno");
 		String lineSn = request.getParameter("lineSn");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String result = request.getParameter("result");
 		String oldOprno = oprno;
 		if(oldOprno.length()==5){

+ 1 - 1
src/main/java/com/jeesite/modules/mes/web/MesProductRivetController.java

@@ -113,7 +113,7 @@ public class MesProductRivetController extends BaseController {
 		String sn = request.getParameter("sn");
 		String lineSn = request.getParameter("lineSn");
 		String oprno = request.getParameter("oprno");
-		String userCode = request.getParameter("ucode");
+		String userCode = CommonUitl.resolveUserCode(request.getParameter("ucode"));
 		String params = request.getParameter("params");
 
 		if (StringUtils.isEmpty(sn) || StringUtils.isEmpty(lineSn)

+ 81 - 0
src/main/java/com/jeesite/modules/sys/log/LogJsonResultAdvice.java

@@ -0,0 +1,81 @@
+package com.jeesite.modules.sys.log;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.PropertyFilter;
+import com.jeesite.common.lang.StringUtils;
+
+/**
+ * 拦截 Controller JSON 返回值,写入访问日志的「返回参数」(对齐若依操作日志)。
+ */
+@ControllerAdvice
+public class LogJsonResultAdvice implements ResponseBodyAdvice<Object> {
+
+	private static final int MAX_LENGTH = 2000;
+	private static final Set<String> EXCLUDE_PROPERTIES = new HashSet<String>(Arrays.asList(
+			"password", "oldPassword", "newPassword", "confirmPassword", "passwd"));
+
+	@Override
+	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+		return true;
+	}
+
+	@Override
+	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
+			Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
+			ServerHttpResponse response) {
+		if (body == null || body instanceof Resource || body instanceof byte[] || body instanceof InputStream
+				|| body instanceof javax.servlet.ServletResponse) {
+			return body;
+		}
+		if (selectedContentType != null) {
+			String type = selectedContentType.toString();
+			if (type.contains("html") || type.contains("octet-stream") || type.contains("image/")
+					|| type.contains("excel") || type.contains("spreadsheet")) {
+				return body;
+			}
+		}
+		String json = toJson(body);
+		if (StringUtils.isNotBlank(json) && !looksLikeHtml(json)) {
+			if (json.length() > MAX_LENGTH) {
+				json = json.substring(0, MAX_LENGTH);
+			}
+			LogJsonResultHolder.set(json);
+		}
+		return body;
+	}
+
+	private String toJson(Object body) {
+		if (body instanceof CharSequence) {
+			return body.toString();
+		}
+		try {
+			return JSON.toJSONString(body, new PropertyFilter() {
+				@Override
+				public boolean apply(Object object, String name, Object value) {
+					return name == null || !EXCLUDE_PROPERTIES.contains(name);
+				}
+			});
+		} catch (Exception e) {
+			return String.valueOf(body);
+		}
+	}
+
+	private boolean looksLikeHtml(String json) {
+		String t = json.trim();
+		return t.startsWith("<") && (t.contains("<html") || t.contains("<!DOCTYPE") || t.contains("<div"));
+	}
+}

+ 32 - 0
src/main/java/com/jeesite/modules/sys/log/LogJsonResultHolder.java

@@ -0,0 +1,32 @@
+package com.jeesite.modules.sys.log;
+
+/**
+ * 当前请求的 JSON 返回体,供访问日志落库使用。
+ */
+public final class LogJsonResultHolder {
+
+	private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
+
+	private LogJsonResultHolder() {
+	}
+
+	public static void set(String json) {
+		if (json == null || json.isEmpty()) {
+			HOLDER.remove();
+		} else {
+			HOLDER.set(json);
+		}
+	}
+
+	public static String getAndRemove() {
+		try {
+			return HOLDER.get();
+		} finally {
+			HOLDER.remove();
+		}
+	}
+
+	public static void remove() {
+		HOLDER.remove();
+	}
+}

+ 245 - 0
src/main/java/com/jeesite/modules/sys/log/LogJsonResultSupport.java

@@ -0,0 +1,245 @@
+package com.jeesite.modules.sys.log;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.sys.entity.Log;
+import com.jeesite.modules.sys.utils.LogUtils;
+
+/**
+ * 将 JSON 返回参数写入 js_sys_log.json_result,并在详情页回填到 Log.remarks。
+ */
+@Component
+public class LogJsonResultSupport implements HandlerInterceptor, Filter {
+
+	private static final Logger logger = LoggerFactory.getLogger(LogJsonResultSupport.class);
+
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+	@PostConstruct
+	public void init() {
+		ensureColumn();
+		wrapLogThreadPool();
+	}
+
+	private void ensureColumn() {
+		try {
+			jdbcTemplate.execute(
+					"ALTER TABLE js_sys_log ADD COLUMN json_result longtext NULL COMMENT '返回参数'");
+			logger.info("js_sys_log.json_result 字段已添加");
+		} catch (Exception e) {
+			String msg = e.getMessage() == null ? "" : e.getMessage();
+			if (!msg.contains("Duplicate column") && !msg.contains("duplicate column")) {
+				logger.debug("js_sys_log.json_result: {}", msg);
+			}
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private void wrapLogThreadPool() {
+		try {
+			Field field = LogUtils.class.getDeclaredField("logThreadPool");
+			field.setAccessible(true);
+			ExecutorService original = (ExecutorService) field.get(null);
+			if (original instanceof JsonResultLogExecutor) {
+				return;
+			}
+			field.set(null, new JsonResultLogExecutor(original));
+			logger.info("已接入访问日志返回参数记录");
+		} catch (Exception e) {
+			logger.warn("接入访问日志返回参数记录失败: {}", e.getMessage());
+		}
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+			ModelAndView modelAndView) {
+		if (modelAndView == null || modelAndView.getModel() == null) {
+			return;
+		}
+		Object logObj = modelAndView.getModel().get("log");
+		if (!(logObj instanceof Log)) {
+			return;
+		}
+		Log log = (Log) logObj;
+		if (StringUtils.isBlank(log.getId())) {
+			return;
+		}
+		try {
+			List<String> rows = jdbcTemplate.query(
+					"SELECT json_result FROM js_sys_log WHERE id = ?",
+					new RowMapper<String>() {
+						@Override
+						public String mapRow(ResultSet rs, int rowNum) throws SQLException {
+							return rs.getString(1);
+						}
+					}, log.getId());
+			if (rows != null && !rows.isEmpty() && StringUtils.isNotBlank(rows.get(0))) {
+				log.setRemarks(rows.get(0));
+			}
+		} catch (Exception e) {
+			logger.debug("读取访问日志返回参数失败: {}", e.getMessage());
+		}
+	}
+
+	@Override
+	public void init(FilterConfig filterConfig) {
+	}
+
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+			throws IOException, ServletException {
+		try {
+			chain.doFilter(request, response);
+		} finally {
+			LogJsonResultHolder.remove();
+		}
+	}
+
+	@Override
+	public void destroy() {
+	}
+
+	private void attachAndPersist(Runnable task, String json) {
+		if (StringUtils.isBlank(json) || task == null) {
+			return;
+		}
+		try {
+			if (!task.getClass().getName().contains("SaveLogThread")) {
+				return;
+			}
+			Field logField = task.getClass().getDeclaredField("log");
+			logField.setAccessible(true);
+			Log log = (Log) logField.get(task);
+			if (log == null || StringUtils.isBlank(log.getId())) {
+				return;
+			}
+			jdbcTemplate.update("UPDATE js_sys_log SET json_result = ? WHERE id = ?", json, log.getId());
+		} catch (Exception e) {
+			logger.debug("保存访问日志返回参数失败: {}", e.getMessage());
+		}
+	}
+
+	private class JsonResultLogExecutor implements ExecutorService {
+		private final ExecutorService delegate;
+
+		JsonResultLogExecutor(ExecutorService delegate) {
+			this.delegate = delegate;
+		}
+
+		private Runnable wrap(Runnable task) {
+			final String json = LogJsonResultHolder.getAndRemove();
+			if (StringUtils.isBlank(json)) {
+				return task;
+			}
+			return new Runnable() {
+				@Override
+				public void run() {
+					try {
+						task.run();
+					} finally {
+						attachAndPersist(task, json);
+					}
+				}
+			};
+		}
+
+		@Override
+		public void execute(Runnable command) {
+			delegate.execute(wrap(command));
+		}
+
+		@Override
+		public Future<?> submit(Runnable task) {
+			return delegate.submit(wrap(task));
+		}
+
+		@Override
+		public <T> Future<T> submit(Runnable task, T result) {
+			return delegate.submit(wrap(task), result);
+		}
+
+		@Override
+		public <T> Future<T> submit(Callable<T> task) {
+			return delegate.submit(task);
+		}
+
+		@Override
+		public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+			return delegate.invokeAll(tasks);
+		}
+
+		@Override
+		public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+				throws InterruptedException {
+			return delegate.invokeAll(tasks, timeout, unit);
+		}
+
+		@Override
+		public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+			return delegate.invokeAny(tasks);
+		}
+
+		@Override
+		public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+				throws InterruptedException, ExecutionException, TimeoutException {
+			return delegate.invokeAny(tasks, timeout, unit);
+		}
+
+		@Override
+		public void shutdown() {
+			delegate.shutdown();
+		}
+
+		@Override
+		public List<Runnable> shutdownNow() {
+			return delegate.shutdownNow();
+		}
+
+		@Override
+		public boolean isShutdown() {
+			return delegate.isShutdown();
+		}
+
+		@Override
+		public boolean isTerminated() {
+			return delegate.isTerminated();
+		}
+
+		@Override
+		public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+			return delegate.awaitTermination(timeout, unit);
+		}
+	}
+}

+ 6 - 6
src/main/resources/config/application.yml

@@ -54,12 +54,12 @@ jdbc:
   # Mysql 数据库配置
   type: mysql
   driver: com.mysql.cj.jdbc.Driver
-  url: jdbc:mysql://192.168.16.100:3306/mes_cloud?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
-  #url: jdbc:mysql://127.0.0.1:3306/mes_cloud_199_0y7a?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
-  username: mes_cloud
-  password: Aw3EYLj5GcHnrFm8
-  #username: root
-  #password: 123456
+#  url: jdbc:mysql://192.168.16.100:3306/mes_cloud?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
+  url: jdbc:mysql://127.0.0.1:3306/mes_cloud_199_0y7a?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
+#  username: mes_cloud
+#  password: Aw3EYLj5GcHnrFm8
+  username: root
+  password: 123456
 
   testSql: SELECT 1
 

+ 16 - 9
src/main/resources/mappings/modules/mes/MesProductFswDao.xml

@@ -1,15 +1,22 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.jeesite.modules.mes.dao.MesProductFswDao">
-	
-	<!-- 查询数据
-	<select id="findList" resultType="MesProductFsw">
-		SELECT ${sqlMap.column.toSql()}
-		FROM ${sqlMap.table.toSql()}
+
+	<select id="findGroupList" resultType="com.jeesite.modules.mes.entity.MesProductFsw">
+		SELECT
+			MAX(a.id) AS id,
+			a.sn AS sn,
+			a.line_sn AS lineSn,
+			GROUP_CONCAT(DISTINCT a.oprno ORDER BY a.oprno SEPARATOR ',') AS oprno,
+			COUNT(1) AS recordCount,
+			MAX(a.create_by) AS createBy,
+			MAX(a.create_date) AS createDate
+		FROM mes_product_fsw a
 		<where>
 			${sqlMap.where.toSql()}
 		</where>
-		ORDER BY ${sqlMap.order.toSql()}
-	</select> -->
-	
-</mapper>
+		GROUP BY a.sn, a.line_sn
+		ORDER BY MAX(a.create_date) DESC
+	</select>
+
+</mapper>

+ 1 - 1
src/main/resources/views/modules/mes/mesMaterialCheckRecordForm2.html

@@ -230,7 +230,7 @@ var vm = new Vue({
 		nums:${mesMaterialCheckRecord.nums},
 		faNums:${mesMaterialCheckRecord.faNums},
 		state:"1",
-		sampleCount: ${sampleCount!5},
+		sampleCount:${sampleCount!5},
 		options:[]
 	},
 	computed: {

+ 37 - 13
src/main/resources/views/modules/mes/mesProductCmtInfo.html

@@ -30,9 +30,15 @@
                             <th>电压1</th>
                             <th>电流1</th>
                             <th>送丝速度1</th>
+                            <th>报警代码1</th>
+                            <th>程序名1</th>
+                            <th>设备编码1</th>
                             <th>电压2</th>
                             <th>电流2</th>
                             <th>送丝速度2</th>
+                            <th>报警代码2</th>
+                            <th>程序名2</th>
+                            <th>设备编码2</th>
                             <th>记录时间</th>
                         </tr>
                     </thead>
@@ -41,10 +47,16 @@
                             <td>{{ item.V1 }}</td>
                             <td>{{ item.I1 }}</td>
                             <td>{{ item.speed1 }}</td>
+                            <td>{{ item.code1 }}</td>
+                            <td>{{ item.JOB1 }}</td>
+                            <td>{{ item.prodCode1 }}</td>
                             <td>{{ item.V2 }}</td>
                             <td>{{ item.I2 }}</td>
                             <td>{{ item.speed2 }}</td>
-                            <td> {{ item.createDate }}</td>
+                            <td>{{ item.code2 }}</td>
+                            <td>{{ item.JOB2 }}</td>
+                            <td>{{ item.prodCode2 }}</td>
+                            <td>{{ item.createDate }}</td>
                         </tr>
                     </tbody>
                 </table>
@@ -83,21 +95,33 @@
 <script src="${ctxStatic}/echarts/4.2/echarts.min.js"></script>
 <script src="${ctxStatic}/common/elementui2.15.5/vue-2.6.14.js"></script>
 <script>
+    const cell = (values, idx) => {
+        if (!values || idx >= values.length || values[idx] == null) {
+            return '';
+        }
+        const text = String(values[idx]).trim();
+        if (text === '' || text === 'null' || text === 'NaN' || text === 'undefined') {
+            return '';
+        }
+        return text;
+    };
     const convertToArrayOfObjects = (arr) => {
         return arr.map(str => {
-            const values = str.split('|');
+            const values = String(str).split('|');
             return {
-                V1: parseFloat(values[0]),         // 电压1
-                I1: parseFloat(values[1]),         // 电流1
-                speed1: parseFloat(values[2]),    // 送丝速度1
-                code1: parseInt(values[3], 10),   // 错误码1
-                JOB1: parseInt(values[4], 10),    // JOB号1
-                V2: parseFloat(values[5]),         // JOB号1
-                I2: parseFloat(values[6]),         // 电压2
-                speed2: parseFloat(values[7]),    // 电流2
-                code2: parseInt(values[8], 10),   // 错误码2
-                JOB2: parseInt(values[9], 10),    // JOB号2
-                createDate: values[10]             // 记录时间
+                V1: parseFloat(values[0]),
+                I1: parseFloat(values[1]),
+                speed1: parseFloat(values[2]),
+                code1: cell(values, 3),
+                JOB1: cell(values, 4),
+                V2: parseFloat(values[5]),
+                I2: parseFloat(values[6]),
+                speed2: parseFloat(values[7]),
+                code2: cell(values, 8),
+                JOB2: cell(values, 9),
+                createDate: cell(values, 10),
+                prodCode1: cell(values, 11),
+                prodCode2: cell(values, 12)
             };
         });
     };

+ 63 - 0
src/main/resources/views/modules/mes/mesProductFswInfo.html

@@ -0,0 +1,63 @@
+<% layout('/layouts/default.html', {title: 'FSW过程参数详情', libs: ['validate']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa icon-note"></i> ${text('FSW过程参数详情')}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<div class="box-body">
+			<div class="form-unit">${text('基本信息')}</div>
+			<table class="table table-bordered">
+				<tr>
+					<th style="width:120px;background-color:#d2d6de;">工件码</th>
+					<td>${mesProductFsw.sn!''}</td>
+					<th style="width:120px;background-color:#d2d6de;">产线编号</th>
+					<td>${mesProductFsw.lineSn!''}</td>
+				</tr>
+			</table>
+			<div class="form-unit">${text('参数明细')}</div>
+			<table class="table table-bordered text-center">
+				<thead>
+					<tr>
+						<th class="text-center" style="background-color:#d2d6de;">工位号</th>
+						<th class="text-center" style="background-color:#d2d6de;">主轴转速</th>
+						<th class="text-center" style="background-color:#d2d6de;">进给速度</th>
+						<th class="text-center" style="background-color:#d2d6de;">焊接压力</th>
+						<th class="text-center" style="background-color:#d2d6de;">操作人员</th>
+						<th class="text-center" style="background-color:#d2d6de;">日期</th>
+					</tr>
+				</thead>
+				<tbody>
+				<% if(detailList != null && detailList.~size > 0){ %>
+					<% for(item in detailList){ %>
+					<tr>
+						<td class="text-center">${item.oprno!''}</td>
+						<td class="text-center">${item.spindleSpeed!''}</td>
+						<td class="text-center">${item.feedRate!''}</td>
+						<td class="text-center">${item.pressure!''}</td>
+						<td class="text-center">${item.createBy!''}</td>
+						<td class="text-center">${item.createDate != null ? @DateUtils.formatDateTime(item.createDate) : ''}</td>
+					</tr>
+					<% } %>
+				<% } else { %>
+					<tr><td colspan="6" class="text-center text-muted">${text('暂无数据')}</td></tr>
+				<% } %>
+				</tbody>
+			</table>
+		</div>
+		<div class="box-footer">
+			<div class="row">
+				<div class="col-sm-offset-2 col-sm-10">
+					<button type="button" class="btn btn-sm btn-default" id="btnCancel"
+							onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}
+					</button>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+<% } %>

+ 16 - 17
src/main/resources/views/modules/mes/mesProductFswList.html

@@ -64,26 +64,25 @@
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
-		{header:'${text("工件码")}', name:'sn', index:'a.sn', width:150, align:"center", frozen:true},
-		{header:'${text("产线编号")}', name:'lineSn', index:'a.line_sn', width:150, align:"center"},
-		{header:'${text("工位号")}', name:'oprno', index:'a.oprno', width:150, align:"center"},
-		{header:'${text("主轴转速")}', name:'spindleSpeed', index:'a.spindle_speed', width:150, align:"center"},
-		{header:'${text("进给速度")}', name:'feedRate', index:'a.feed_rate', width:150, align:"center"},
-		{header:'${text("焊接压力")}', name:'pressure', index:'a.pressure', width:150, align:"center"},
-		{header:'${text("操作人员")}', name:'createBy', index:'a.create_by', width:150, align:"center"},
-		{header:'${text("日期")}', name:'createDate', index:'a.create_date', width:150, align:"center"},
-		// {header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
-		// 	var actions = [];
-		// 	//<% if(hasPermi('mes:mesProductFsw:edit')){ %>
-		// 		actions.push('<a href="${ctx}/mes/mesProductFsw/form?id='+row.id+'" class="btnList" title="${text("编辑FSW过程参数")}"><i class="fa fa-pencil"></i></a>&nbsp;');
-		// 	//<% } %>
-		// 	return actions.join('');
-		// }}
+		{header:'${text("工件码")}', name:'sn', index:'a.sn', width:200, align:"center", frozen:true, formatter: function(val, obj, row, act){
+			var sn = encodeURIComponent(row.sn || '');
+			var lineSn = encodeURIComponent(row.lineSn || '');
+			return '<a href="${ctx}/mes/mesProductFsw/info?sn='+sn+'&lineSn='+lineSn+'" class="btnList" data-title="${text("FSW过程参数详情")}">'+(val||row.id)+'</a>';
+		}},
+		{header:'${text("产线编号")}', name:'lineSn', index:'a.line_sn', width:120, align:"center"},
+		{header:'${text("工位号")}', name:'oprno', index:'a.oprno', width:180, align:"center"},
+		{header:'${text("记录数")}', name:'recordCount', width:90, align:"center"},
+		{header:'${text("操作人员")}', name:'createBy', index:'a.create_by', width:120, align:"center"},
+		{header:'${text("日期")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
+		{header:'${text("操作")}', name:'actions', width:80, align:"center", formatter: function(val, obj, row, act){
+			var sn = encodeURIComponent(row.sn || '');
+			var lineSn = encodeURIComponent(row.lineSn || '');
+			return '<a href="${ctx}/mes/mesProductFsw/info?sn='+sn+'&lineSn='+lineSn+'" class="btnList" title="${text("查看详情")}" data-title="${text("FSW过程参数详情")}"><i class="fa fa-fw fa-eye"></i></a>';
+		}}
 	],
 	frozenCols: true,
-	// 加载成功后执行事件
 	ajaxSuccess: function(data){
 		
 	}
 });
-</script>
+</script>

+ 2 - 0
src/main/resources/views/modules/mes/mesProductRecordWork.html

@@ -47,8 +47,10 @@ $('#dataGrid').dataGrid({
 		{header:'${text("工位号")}', name:'oprno', index:'a.oprno', width:90, align:"center"},
 		{header:'${text("作业人员")}', name:'updateBy', index:'a.update_by', width:150, align:"center"},
 		{header:'${text("日期")}', name:'updateDate', index:'a.update_date', width:160, align:"center" },
+		<% if(showQmColumns){ %>
 		{header:'${text("测试压")}', name:'testPressure', index:'a.testPressure', width:120, align:"center", sortable:false},
 		{header:'${text("泄漏值")}', name:'leakVal', index:'a.leakVal', width:120, align:"center", sortable:false},
+		<% } %>
 		{header:'${text("工位结果")}', name:'content', index:'a.content', width:100, align:"center"},
 	],
 	// treeGrid: true,			// 启用树结构表格

+ 364 - 0
src/main/resources/views/modules/sys/logForm.html

@@ -0,0 +1,364 @@
+<% layout('/layouts/default.html', {title: '访问日志详细', libs: ['validate']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa fa-file-text-o"></i> ${text('访问日志详细')}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<div class="box-body">
+			<div class="detail-wrap">
+				<!-- 基本信息 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-info-circle"></i> ${text('基本信息')}</div>
+					<div class="row detail-row">
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('操作模块')}</span>
+								<span class="detail-value">${isBlank(log.logTitle) ? '-' : log.logTitle}</span>
+							</div>
+						</div>
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('日志类型')}</span>
+								<span class="detail-value">${@DictUtils.getDictLabel("sys_log_type", log.logType, "-")}</span>
+							</div>
+						</div>
+					</div>
+					<div class="row detail-row">
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('操作时间')}</span>
+								<span class="detail-value">${log.createDate != null ? @DateUtils.formatDateTime(log.createDate) : '-'}</span>
+							</div>
+						</div>
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('执行状态')}</span>
+								<span class="detail-value">
+									<% if(@Global.YES.equals(log.isException)){ %>
+										<span class="log-tag log-tag-danger">${text('异常')}</span>
+									<% } else { %>
+										<span class="log-tag log-tag-success">${text('正常')}</span>
+									<% } %>
+								</span>
+							</div>
+						</div>
+					</div>
+					<% if(isNotBlank(log.bizType) || isNotBlank(log.bizKey)){ %>
+					<div class="row detail-row">
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('业务类型')}</span>
+								<span class="detail-value">${isBlank(log.bizType) ? '-' : log.bizType}</span>
+							</div>
+						</div>
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('业务主键')}</span>
+								<span class="detail-value">${isBlank(log.bizKey) ? '-' : log.bizKey}</span>
+							</div>
+						</div>
+					</div>
+					<% } %>
+				</div>
+
+				<!-- 操作人员 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-user"></i> ${text('操作人员')}</div>
+					<div class="row detail-row">
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('操作人员')}</span>
+								<span class="detail-value">${isBlank(log.createByName) ? '-' : log.createByName}</span>
+							</div>
+						</div>
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('操作账号')}</span>
+								<span class="detail-value">${isBlank(log.createBy) ? '-' : log.createBy}</span>
+							</div>
+						</div>
+					</div>
+					<div class="row detail-row">
+						<div class="col-xs-12">
+							<div class="detail-item">
+								<span class="detail-label">${text('操作地址')}</span>
+								<span class="detail-value">
+									${isBlank(log.remoteAddr) ? '-' : log.remoteAddr}
+									<% if(isNotBlank(log.deviceName) || isNotBlank(log.browserName)){ %>
+										<span class="detail-location">${log.deviceName!} ${log.browserName!}</span>
+									<% } %>
+								</span>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<!-- 请求信息 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-exchange"></i> ${text('请求信息')}</div>
+					<div class="row detail-row">
+						<div class="col-xs-12">
+							<div class="detail-item">
+								<span class="detail-label">${text('请求地址')}</span>
+								<span class="detail-value">
+									<% if(isNotBlank(log.requestMethod)){ %>
+										<span class="method-tag method-${log.requestMethod}">${log.requestMethod}</span>
+									<% } %>
+									${log.serverAddr!}${log.requestUri!}
+								</span>
+							</div>
+						</div>
+					</div>
+					<div class="row detail-row">
+						<div class="col-xs-12">
+							<div class="detail-item">
+								<span class="detail-label">${text('用户代理')}</span>
+								<span class="detail-value mono">${isBlank(log.userAgent) ? '-' : log.userAgent}</span>
+							</div>
+						</div>
+					</div>
+					<div class="row detail-row">
+						<div class="col-xs-12 col-sm-6">
+							<div class="detail-item">
+								<span class="detail-label">${text('消耗时间')}</span>
+								<span class="detail-value">${isBlank(log.executeTimeFormat) ? '-' : log.executeTimeFormat}</span>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<!-- 请求参数 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-cloud-upload"></i> ${text('请求参数')}</div>
+					<div class="code-body">
+						<div class="code-wrap">
+							<div class="code-action">
+								<button type="button" class="btn btn-xs btn-default" onclick="copyCode('requestParamsPre')"><i class="fa fa-files-o"></i> ${text('复制')}</button>
+							</div>
+							<pre class="code-pre" id="requestParamsPre">${isBlank(log.requestParams) ? '(无数据)' : log.requestParams}</pre>
+						</div>
+					</div>
+				</div>
+
+				<!-- 返回参数 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-cloud-download"></i> ${text('返回参数')}</div>
+					<div class="code-body">
+						<div class="code-wrap">
+							<div class="code-action">
+								<button type="button" class="btn btn-xs btn-default" onclick="copyCode('jsonResultPre')"><i class="fa fa-files-o"></i> ${text('复制')}</button>
+							</div>
+							<pre class="code-pre" id="jsonResultPre">${isBlank(log.remarks) ? '(无数据)' : log.remarks}</pre>
+						</div>
+					</div>
+				</div>
+
+				<% if(isNotBlank(log.diffModifyData)){ %>
+				<!-- 差异修改数据 -->
+				<div class="detail-card">
+					<div class="detail-card-title"><i class="fa fa-cloud-download"></i> ${text('差异修改数据')}</div>
+					<div class="code-body">
+						<div class="code-wrap">
+							<div class="diff-html" id="diffModifyData">${log.diffModifyData}</div>
+						</div>
+					</div>
+				</div>
+				<% } %>
+
+				<% if(@Global.YES.equals(log.isException)){ %>
+				<!-- 异常信息 -->
+				<div class="detail-card">
+					<div class="detail-card-title error-title"><i class="fa fa-warning"></i> ${text('异常信息')}</div>
+					<div class="error-body">
+						<div class="error-msg">${isBlank(log.exceptionInfo) ? '(无数据)' : log.exceptionInfo}</div>
+					</div>
+				</div>
+				<% } %>
+			</div>
+		</div>
+		<div class="box-footer">
+			<div class="row">
+				<div class="col-sm-offset-2 col-sm-10">
+					<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="closeDetail()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+<% } %>
+<style>
+.detail-wrap { padding: 4px 8px 8px; }
+.detail-card {
+	border: 1px solid #ebeef5;
+	border-radius: 6px;
+	margin-bottom: 14px;
+	overflow: hidden;
+}
+.detail-card-title {
+	background: #f7f9fb;
+	padding: 8px 16px;
+	font-size: 13px;
+	font-weight: 600;
+	color: #333;
+	border-bottom: 1px solid #ebeef5;
+}
+.detail-card-title i { margin-right: 6px; color: #3c8dbc; }
+.detail-row { margin: 0; }
+.detail-item {
+	display: flex;
+	align-items: flex-start;
+	padding: 10px 16px;
+	font-size: 13px;
+	border-bottom: 1px solid #f5f7fa;
+	min-height: 38px;
+}
+.detail-item:last-child { border-bottom: none; }
+.detail-label {
+	flex-shrink: 0;
+	width: 72px;
+	color: #909399;
+	margin-right: 12px;
+	line-height: 1.6;
+}
+.detail-value {
+	color: #303133;
+	flex: 1;
+	word-break: break-all;
+	line-height: 1.6;
+}
+.detail-location { color: #999; font-size: 12px; margin-left: 8px; }
+.mono { font-family: Consolas, "SFMono-Regular", "Courier New", monospace; font-size: 12px; }
+.method-tag {
+	display: inline-block;
+	padding: 1px 7px;
+	border-radius: 3px;
+	font-size: 11px;
+	font-weight: 700;
+	margin-right: 6px;
+	vertical-align: middle;
+	line-height: 16px;
+}
+.method-GET { background: #e8f5e9; color: #27ae60; }
+.method-POST { background: #e3f2fd; color: #1565c0; }
+.method-PUT { background: #fff3e0; color: #e65100; }
+.method-DELETE { background: #ffebee; color: #c62828; }
+.log-tag {
+	display: inline-block;
+	padding: 1px 8px;
+	border-radius: 3px;
+	font-size: 12px;
+	line-height: 18px;
+}
+.log-tag-success { background: #e8f5e9; color: #27ae60; }
+.log-tag-danger { background: #ffebee; color: #c62828; }
+.code-body { padding: 14px; }
+.code-wrap {
+	background: #f7f9fb;
+	border: 1px solid #e8ecf0;
+	border-radius: 4px;
+	overflow: hidden;
+	max-height: 260px;
+	position: relative;
+}
+.code-action {
+	position: absolute;
+	top: 8px;
+	right: 8px;
+	z-index: 2;
+}
+.code-action .btn {
+	background: rgba(255,255,255,.92);
+	border: 1px solid #dcdcdc;
+}
+.code-pre, .diff-html {
+	margin: 0;
+	padding: 12px 14px;
+	font-size: 12px;
+	line-height: 1.6;
+	font-family: Consolas, "SFMono-Regular", "Courier New", monospace;
+	color: #444;
+	white-space: pre-wrap;
+	word-break: break-all;
+	overflow: auto;
+	max-height: 240px;
+	display: block;
+}
+.diff-html { font-family: inherit; }
+.error-title { color: #c0392b !important; }
+.error-title i { color: #c0392b !important; }
+.error-body { padding: 12px 16px; }
+.error-msg {
+	background: #fff8f8;
+	border-left: 3px solid #e74c3c;
+	border-radius: 3px;
+	padding: 8px 12px;
+	color: #c0392b;
+	font-size: 12px;
+	line-height: 1.7;
+	word-break: break-all;
+	white-space: pre-wrap;
+	max-height: 320px;
+	overflow: auto;
+}
+.skin-dark .detail-card { border-color: #2a2a2a; }
+.skin-dark .detail-card-title { background: #1a1a1a; color: #cfcfcf; border-bottom-color: #2a2a2a; }
+.skin-dark .detail-item { border-bottom-color: #222; }
+.skin-dark .detail-label { color: #888; }
+.skin-dark .detail-value { color: #cfcfcf; }
+.skin-dark .code-wrap { background: #1a1a1a; border-color: #2a2a2a; }
+.skin-dark .code-pre, .skin-dark .diff-html { color: #bbb; }
+.skin-dark .error-msg { background: #2a1515; }
+</style>
+<script>
+function formatJson(str) {
+	if (!str || str === '(无数据)') return '(无数据)';
+	try { return JSON.stringify(JSON.parse(str), null, 2); } catch (e) { return str; }
+}
+function copyCode(elId) {
+	var text = formatJson($('#' + elId).text());
+	function ok(){ js.showMessage('${text("已复制")}'); }
+	if (navigator.clipboard && navigator.clipboard.writeText) {
+		navigator.clipboard.writeText(text).then(ok).catch(fallback);
+	} else {
+		fallback();
+	}
+	function fallback() {
+		var ta = document.createElement('textarea');
+		ta.value = text;
+		document.body.appendChild(ta);
+		ta.select();
+		try { document.execCommand('copy'); ok(); } catch (e) {}
+		document.body.removeChild(ta);
+	}
+}
+function closeDetail() {
+	if (parent && parent.layer && window.name) {
+		try {
+			var idx = parent.layer.getFrameIndex(window.name);
+			if (idx) { parent.layer.close(idx); return; }
+		} catch (e) {}
+	}
+	js.closeCurrentTabPage();
+}
+$(function(){
+	['#requestParamsPre', '#jsonResultPre'].forEach(function(sel){
+		var $pre = $(sel);
+		if ($pre.length) {
+			$pre.text(formatJson($pre.text()));
+		}
+	});
+	if (parent && parent.layer && window.name) {
+		try {
+			if (parent.layer.getFrameIndex(window.name)) {
+				$('.box-header').hide();
+				$('#btnCancel').closest('.box-footer').hide();
+			}
+		} catch (e) {}
+	}
+});
+</script>

+ 147 - 0
src/main/resources/views/modules/sys/logList.html

@@ -0,0 +1,147 @@
+<% layout('/layouts/default.html', {title: '访问日志', libs: ['dataGrid']}){ %>
+<div class="main-content">
+	<div class="box box-main nav-tabs-custom nav-main">
+		<div class="box-header">
+			<div class="box-tools pull-right">
+				<a href="#" class="btn btn-default" id="btnSearch" title="${text('查询')}"><i class="fa fa-filter"></i> ${text('查询')}</a>
+				<a href="#" class="btn btn-default" id="btnSetting" title="${text('设置')}"><i class="fa fa-navicon"></i></a>
+			</div>
+		</div>
+		<ul class="nav nav-tabs">
+			<li class="${isBlank(parameter.logType) ? 'active' : ''}"><a href="${
+				ctx}/sys/log/list?logType="><i class="fa fa-bug"></i> ${text('访问日志')}</a></li>
+			<% var sysLogType = @DictUtils.getDictList('sys_log_type'); for(var dict in sysLogType){ %>
+			<li class="${parameter.logType! == dict.dictValue ? 'active' : ''}"><a href="${
+				ctx}/sys/log/list?logType=${dict.dictValue}">${dict.dictLabel}</a></li>
+			<% } %>
+		</ul>
+		<div class="box-body">
+			<#form:form id="searchForm" model="${log}" action="${ctx}/sys/log/listData" method="post" class="form-inline "
+					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
+				<div class="form-group">
+					<label class="control-label">${text('日志标题')}:</label>
+					<div class="control-inline">
+						<#form:input path="logTitle" maxlength="500" class="form-control width-90"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('请求地址')}:</label>
+					<div class="control-inline">
+						<#form:input path="requestUri" maxlength="255" class="form-control width-90"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('日志类型')}:</label>
+					<div class="control-inline width-date">
+						<#form:select path="logType" dictType="sys_log_type" blankOption="true" class="form-control required " />
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('操作用户')}:</label>
+					<div class="control-inline width-120">
+						<#form:listselect id="userSelect" title="${text('用户选择')}" path="createBy"
+							url="${ctx}/sys/empUser/empUserSelect" allowClear="false" 
+							checkbox="false" itemCode="userCode" itemName="userName"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('是否异常')}:</label>
+					<div class="control-inline width-60">
+						<#form:select path="isException" dictType="sys_yes_no" blankOption="true" class="form-control"/>
+					</div>
+				</div>
+				<div class="clearfix"></div>
+				<div class="form-group">
+					<label class="control-label">${text('业务类型')}:</label>
+					<div class="control-inline">
+						<#form:input path="bizType" maxlength="64" class="form-control width-90"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('业务主键')}:</label>
+					<div class="control-inline">
+						<#form:input path="bizKey" maxlength="64" class="form-control width-90"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('操作时间')}:</label>
+					<div class="control-inline">
+						<#form:input path="createDate_gte" readonly="true" maxlength="20" class="form-control laydate width-date"
+							dataFormat="date" data-type="date" data-format="yyyy-MM-dd" data-done="createDate_lte.click()"/> -
+						<#form:input path="createDate_lte" readonly="true" maxlength="20" class="form-control laydate width-date"
+							dataFormat="date" data-type="date" data-format="yyyy-MM-dd"/>
+					</div>
+					<div class="control-inline" style="width:75px;">
+						<select onchange="js.quickSelectDate(this.value, 'createDate_gte', 'createDate_lte');" class="form-control">
+							<option value="0">&nbsp;</option><option value="1">${text('今日')}</option>
+							<option value="2">${text('本周')}</option><option value="2|7">${text('上周')}</option>
+							<option value="3">${text('本月')}</option><option value="4">${text('本季度')}</option>
+							<option value="5">${text('上1个月')}</option><option value="5|3">${text('上3个月')}</option>
+							<option value="1|7">${text('近1个周')}</option><option value="6">${text('近1个月')}</option>
+							<option value="6|3">${text('近3个月')}</option>
+						</select>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('客户端IP')}:</label>
+					<div class="control-inline">
+						<#form:input path="remoteAddr" maxlength="255" class="form-control width-90"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
+					<button type="reset" class="btn btn-default btn-sm">${text('重置')}</button>
+				</div>
+			</#form:form>			
+			<table id="dataGrid"></table>
+			<div id="dataGridPage"></div>
+		</div>
+	</div>
+</div>
+<% } %>
+<script>
+function showLogDetail(id) {
+	js.layer.open({
+		type: 2,
+		title: '${text("访问日志详细")}',
+		shadeClose: true,
+		shade: 0.3,
+		maxmin: true,
+		area: ['800px', '86%'],
+		content: '${ctx}/sys/log/form?id=' + id
+	});
+}
+$('#dataGrid').dataGrid({
+	searchForm: $("#searchForm"),
+	columnModel: [
+		{header:'${text("日志标题")}', name:'logTitle', index:'a.log_title', width:200, align:"left", frozen:true, formatter: function(val, obj, row, act){
+			return '<a href="javascript:" onclick="showLogDetail(\''+row.id+'\')">'+(val||row.id)+'</a>';
+		}},
+		{header:'${text("请求地址")}', name:'requestUri', index:'a.request_uri', width:260, align:"left", formatter: function(val, obj, row, act){
+			return '<span title="['+row.requestMethod+'] '+row.serverAddr+row.requestUri+'">'+row.requestUri+'</span>';
+		}},
+		{header:'${text("日志类型")}', name:'logType', index:'a.log_type', width:100, align:"center", formatter: function(val, obj, row, act){
+			return js.getDictLabel(${@DictUtils.getDictListJson('sys_log_type')}, val, '未知', true);
+		}},
+		{header:'${text("操作用户")}', name:'createByName', index:'a.create_by_name', width:100, align:"center", formatter: function(val, obj, row, act){
+			return '<span title="${text("账号")}:'+row.createBy+'">'+ row.createByName;
+		}},
+		{header:'${text("异常")}', name:'isException', index:'a.is_exception', width:60, align:"center", formatter: function(val, obj, row, act){
+			return js.getDictLabel(${@DictUtils.getDictListJson('sys_yes_no')}, val, '未知', true);
+		}},
+		{header:'${text("业务类型")}', name:'bizType', index:'a.biz_type', width:90, align:"center"},
+		{header:'${text("业务主键")}', name:'bizKey', index:'a.biz_key', width:90, align:"center"},
+		{header:'${text("操作时间")}', name:'createDate', index:'a.create_date', width:100, align:"center"},
+		{header:'${text("客户端IP")}', name:'remoteAddr', index:'a.remote_addr', width:100, align:"center"},
+		{header:'${text("设备名称")}', name:'deviceName', index:'a.device_name', width:100, align:"center"},
+		{header:'${text("浏览器名")}', name:'browserName', index:'a.browser_name', width:100, align:"center"},
+		{header:'${text("响应时间")}', name:'executeTimeFormat', index:'a.execute_time', width:100, align:"center"},
+		{header:'${text("操作")}', name:'actions', width:80, align:"center", formatter: function(val, obj, row, act){
+			return '<a href="javascript:" onclick="showLogDetail(\''+row.id+'\')" title="${text("详细")}"><i class="fa fa-search-plus"></i> ${text("详细")}</a>';
+		}}
+	],
+	ajaxSuccess: function(data){
+		
+	}
+});
+</script>

+ 0 - 174
views/modules/sys/user/empUserFormAuthDataScope.html

@@ -1,174 +0,0 @@
-<% layout('/layouts/default.html', {title: '用户管理', libs: ['validate', 'zTree']}){ %>
-<div class="main-content">
-	<div class="box box-main">
-		<div class="box-header">
-			<div class="box-title">
-				<i class="fa icon-people"></i> ${text('用户分配数据权限')}
-			</div>
-			<div class="box-tools pull-right">
-				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
-			</div>
-		</div>
-		<#form:form id="inputForm" model="${empUser}" action="${ctx}/sys/empUser/saveAuthDataScope" method="post" class="form-horizontal">
-			<#form:hidden path="userCode"/>
-			<div class="box-body"><br/>
-				<div class="row">
-					<div class="col-xs-6">
-						<div class="form-group">
-							<label class="control-label col-sm-4" title="">
-								<span class="required ">*</span> ${text('登录账号')}:<i class="fa icon-question hide"></i></label>
-							<div class="col-sm-8">
-								<#form:input path="loginCode" maxlength="32" readonly="${!empUser.isNewRecord}" class="form-control required "/>
-							</div>
-						</div>
-					</div>
-					<div class="col-xs-6">
-						<div class="form-group">
-							<label class="control-label col-sm-4" title="">
-								<span class="required ">*</span> ${text('用户昵称')}:<i class="fa icon-question hide"></i></label>
-							<div class="col-sm-8">
-								<#form:input path="userName" maxlength="32" readonly="${!empUser.isNewRecord}" class="form-control required "/>
-							</div>
-						</div>
-					</div>
-				</div>
-				<div class="form-unit">${text('数据权限')}</div>
-				<div id="dataScopeTrees"></div>
-				<script id="dataScopeTpl" type="text/template">
-					<div class="pull-left" style="padding:0 15px;min-width:300px;">
-						<div class="box box-solid box-trees">
-							<div class="box-header">
-								<div class="box-title icheck">
-									<label><input type="checkbox" id="checkall_{{d.key}}"
-										class="checkall"/> {{d.label}}</label>
-								</div>
-								<div class="box-tools pull-right" style="top:8px;">
-									<a class="btn btn-box-tool" id="expand_{{d.key}}"
-										value="dataScopeTree_{{d.key}}" >${text('展开')}</a>/<a
-										class="btn btn-box-tool" id="collapse_{{d.key}}"
-										value="dataScopeTree_{{d.key}}" >${text('折叠')}</a>
-								</div>
-							</div>
-							<div class="box-body">
-								<div id="dataScopeTree_{{d.key}}" class="ztree"></div>
-							</div>
-						</div>
-					</div>
-				</script>
-			    <#form:hidden name="userDataScopeListJson"/>
-			</div>
-			<div class="box-footer">
-				<div class="row">
-					<div class="col-sm-offset-2 col-sm-10">
-						<% if (hasPermi('sys:empUser:authDataScope')){ %>
-							<button type="submit" class="btn btn-sm btn-primary" id="btnSubmit"><i class="fa fa-check"></i> ${text('保 存')}</button>&nbsp;
-						<% } %>
-						<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
-					</div>
-				</div>
-			</div>
-		</#form:form>
-	</div>
-</div>
-<% } %>
-<script>
-$("#inputForm").validate({
-	submitHandler: function(form){
-		// 获取数据权限数据
-		var dataScopeData = [];
-		$.each(dataScopeTrees, function(key, dataScopeTree){
-			var treeNodes = dataScopeTree.getCheckedNodes(true);
-			for(var i=0; i<treeNodes.length; i++) {
-				dataScopeData.push({
-					ctrlType: key, ctrlData: treeNodes[i].id
-				});
-			}
-		});
-		$("#userDataScopeListJson").val(JSON.stringify(dataScopeData));
-		// 提交表单数据
-		js.ajaxSubmitForm($(form), function(data){
-			js.showMessage(data.message);
-			if(data.result == Global.TRUE){
-				js.closeCurrentTabPage(function(contentWindow){
-					contentWindow.page();
-				});
-			}
-		}, "json");
-    }
-});
-//加载数据权限树结构
-var setting = {
-	check:{enable:true,nocheckInherit:true},
-	view:{selectedMulti:false,nameIsHTML: true},
-	data:{simpleData:{enable:true},key:{title:"title"}},
-	callback:{
-		beforeClick: function (treeId, treeNode, clickFlag) {
-			var tree = $.fn.zTree.getZTreeObj(treeId);
-			tree.checkNode(treeNode, !treeNode.checked, true, true);
-			return false;
-		},
-		onCheck: function (event, treeId, treeNode){ }
-	}
-},
-moduleCodes = '${toJson(moduleCodes)}';
-dataScopes = ${toJson(dataScopes)},
-dataScopeTrees = {}; // 用sysCode分类存储所有菜单树
-for (var i=0; i<dataScopes.length; i++){
-	var dataScope = dataScopes[i];
-	// 验证模块是否开启,如果未开启,则跳过
-	if (moduleCodes.indexOf("\""+dataScope.moduleCode+"\"") == -1){
-		continue;
-	}
-	// 控制权限 ctrlPermi: 0全部  1拥有权限  2管理权限
-	if (!(dataScope.ctrlPermi == '0' || dataScope.ctrlPermi == '1')){
-		continue;
-	}
- 	$('#dataScopeTrees').append(js.template('dataScopeTpl', {
- 		key: dataScope.ctrlType, label: dataScope.ctrlName_${lang()} || dataScope.ctrlName}));
- 	var ctrlDataUrl = dataScope.ctrlDataUrl || '';
-	$.ajax({
-		type: 'POST',
-		url: "${ctx}" + ctrlDataUrl + (ctrlDataUrl.indexOf("?")!=-1?'&':'?') + "___t=" + new Date().getTime(),
-		data: {ctrlPermi: '${ctrlPermi}'},
-		dataType: 'json',
-		async: false,
-		error: function(data){
-			js.showErrorMessage(data.responseText);
-		},
-		success: function(data, status, xhr){
-			// 初始化树结构
-			var tree = $.fn.zTree.init($("#dataScopeTree_"+dataScope.ctrlType), setting, data);
-			tree.setting.check.chkboxType = dataScope.chkboxType;
-			// 默认展开节点(如果级别设置为-1,则:如果有1个根节点,则展开一级节点,否则不展开)
-			$.fn.zTree.expandNodeByLevel(tree, dataScope.expandLevel);
-			// 树结构:全选、取消全选
-			$('#checkall_'+dataScope.ctrlType).iCheck({
-		 		checkboxClass:'icheckbox_minimal-grey'
-		 	}).on('ifChecked ifUnchecked', function(){
-	        	var ctrlType = $(this).attr('ctrlType');
-				if(this.checked){
-					dataScopeTrees[ctrlType].checkAllNodes(true);
-				}else{
-					dataScopeTrees[ctrlType].checkAllNodes(false);
-				}
-			}).attr("ctrlType", dataScope.ctrlType);
-			// 展开和折叠按钮绑定
-			$('#expand_'+dataScope.ctrlType).click(function(){
-				var ctrlType = $(this).attr('ctrlType');
-				dataScopeTrees[ctrlType].expandAll(true);
-			}).attr("ctrlType", dataScope.ctrlType);
-			$('#collapse_'+dataScope.ctrlType).click(function(){
-				var ctrlType = $(this).attr('ctrlType');
-				dataScopeTrees[ctrlType].expandAll(false);
-			}).attr("ctrlType", dataScope.ctrlType);
-			// 将树对象存储到全局数组里
-			dataScopeTrees[dataScope.ctrlType] = tree;
-		}
-	});
-}
-// 默认选择节点
-//<% for(dataScope in userDataScopeList){ %>
-try{dataScopeTrees['${dataScope.ctrlType}'].checkNode(dataScopeTrees['${dataScope.ctrlType}']
-	.getNodeByParam("id","${dataScope.ctrlData}"), true, false);}catch(e){}
-//<% } %>
-</script>