فهرست منبع

个人资格证书相关功能调整

徐滕 6 روز پیش
والد
کامیت
6da1450650
20فایلهای تغییر یافته به همراه5053 افزوده شده و 2 حذف شده
  1. 16 0
      pom.xml
  2. 67 0
      src/main/java/com/jeeplus/modules/sys/service/WorkattachmentService.java
  3. 63 0
      src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffBasicCertificationDao.java
  4. 18 0
      src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffBasicCertificationHistoryDao.java
  5. 20 0
      src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffCertificateDao.java
  6. 72 0
      src/main/java/com/jeeplus/modules/workstaff/entity/WorkStaffCertificate.java
  7. 85 0
      src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicCertificationHistoryService.java
  8. 1766 0
      src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicCertificationService.java
  9. 11 0
      src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicInfoService.java
  10. 30 2
      src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffCertificateService.java
  11. 268 0
      src/main/java/com/jeeplus/modules/workstaff/web/WorkStaffBasicCertificationController.java
  12. 111 0
      src/main/java/com/jeeplus/modules/workstaff/web/WorkStaffBasicCertificationHistoryController.java
  13. 393 0
      src/main/resources/mappings/modules/workstaff/WorkStaffBasicCertificationDao.xml
  14. 108 0
      src/main/resources/mappings/modules/workstaff/WorkStaffBasicCertificationHistoryDao.xml
  15. 87 0
      src/main/resources/mappings/modules/workstaff/WorkStaffCertificateDao.xml
  16. 214 0
      src/main/webapp/webpage/modules/workstaff/cerification/handSignatureForm.jsp
  17. 570 0
      src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationForm.jsp
  18. 291 0
      src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationHistoryList.jsp
  19. 453 0
      src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationList.jsp
  20. 410 0
      src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationView.jsp

+ 16 - 0
pom.xml

@@ -101,6 +101,22 @@
                 </executions>
             </plugin>
         </plugins>
+        <resources>
+            <!-- 强制复制 java 目录下的 ttf 文件 -->
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.ttf</include>
+                </includes>
+            </resource>
+            <!-- 保留原有 resources 目录的资源 -->
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
     </build>
     <!-- 依赖项定义 -->
     <dependencies>

+ 67 - 0
src/main/java/com/jeeplus/modules/sys/service/WorkattachmentService.java

@@ -20,6 +20,7 @@ import com.jeeplus.modules.sys.utils.UserUtils;
 import com.jeeplus.modules.utils.ViewFileUtil;
 import com.jeeplus.modules.workclientinfo.dao.WorkClientAttachmentDao;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientAttachment;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -1222,6 +1223,34 @@ public class WorkattachmentService extends CrudService<WorkattachmentDao, Workat
 	}
 
 	/**
+	 * 数据处理
+	 * @param url
+	 */
+	public static String fileTemporaryLookUrlManageOnPrefix(String url){
+		ViewFileUtil viewFileUtil = new ViewFileUtil();
+		// 判断文件存储方式(0:本地服务器存储。1:云端存储)
+		switch (uploadMode){
+			case "1":
+				url = bosUrl + url;
+				break;
+			case "2":
+				url = aliyunUrl + url;
+				url = new OSSClientUtil().getFileTemporaryLookUrl(url);
+				break;
+			default:
+				//获取本地服务器ip和端口号
+				String serverIPAndPort = viewFileUtil.getServerIPAndPort();
+				//创建展示附件访问方法路径
+				String methodAccessPath = "/a/viewFile/viewFile/getUploadFile?uploadFilePath=";
+				String newUrl = serverIPAndPort + methodAccessPath + url;
+				//变更新的文件查看地址
+				url = newUrl;
+				break;
+		}
+		return url;
+	}
+
+	/**
 	 * 根据存储路径查询文件信息
 	 * @param url
 	 * @return
@@ -1258,6 +1287,44 @@ public class WorkattachmentService extends CrudService<WorkattachmentDao, Workat
 			}
 		}
 	}
+
+	/**
+	 * 数据处理(如果为阿里云文件服务器,则对查看的路径进行处理)
+	 * @param list
+	 */
+	public static void attachmentManageByUrlOnStaffCertificateList(List<WorkStaffCertificate> list){
+		// 判断文件存储方式(0:本地服务器存储。1:云端存储)
+		for (WorkStaffCertificate info : list) {
+			switch (uploadMode){
+				case "1":
+					info.setFilePath(bosUrl + info.getFilePath());
+					break;
+				case "2":
+					info.setFilePath(aliyunUrl + info.getFilePath());
+					info.setTemporaryUrl(new OSSClientUtil().getFileTemporaryLookUrl(info.getFilePath()));
+					break;
+
+			}
+		}
+	}
+
+	/**
+	 * 数据处理(如果为阿里云文件服务器,则对查看的路径进行处理)
+	 * @param list
+	 */
+	public static void attachmentManageByUrlOnStaffCertificate(WorkStaffCertificate workStaffCertificate){
+		// 判断文件存储方式(0:本地服务器存储。1:云端存储)
+		switch (uploadMode){
+			case "1":
+				workStaffCertificate.setFilePath(bosUrl + workStaffCertificate.getFilePath());
+				break;
+			case "2":
+				workStaffCertificate.setFilePath(aliyunUrl + workStaffCertificate.getFilePath());
+				workStaffCertificate.setTemporaryUrl(new OSSClientUtil().getFileTemporaryLookUrl(workStaffCertificate.getFilePath()));
+				break;
+
+		}
+	}
 	/**
 	 * 此方法仅用于知识库灵活表头处
 	 * @param list

+ 63 - 0
src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffBasicCertificationDao.java

@@ -0,0 +1,63 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.modules.workstaff.dao;
+
+import com.jeeplus.common.persistence.CrudDao;
+import com.jeeplus.common.persistence.annotation.MyBatisDao;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificateImport;
+
+import java.util.List;
+
+/**
+ * 执业资格证书DAO接口
+ * @author ssrh
+ * @version 2018-07-27
+ */
+@MyBatisDao
+public interface WorkStaffBasicCertificationDao extends CrudDao<WorkStaffCertificate> {
+    int updateFalt(String id);
+
+    /**
+     * 根据用户id查询对应数据信息
+     * @param userId
+     * @return
+     */
+    List<WorkStaffCertificate> getByUserId(String userId);
+    /**
+     * 批量添加职业资格信息添加
+     * @param info
+     */
+    void saveImport(WorkStaffCertificateImport info);
+
+    /**
+     * 修改批量添加职业资格信息
+     * @param info
+     */
+    void updateImport(WorkStaffCertificateImport info);
+
+    /**
+     * 修改批量添加职业资格信息
+     * @param info
+     */
+    void updateCertificateValidity(WorkStaffCertificate info);
+
+    /**
+     * 查询所有的一级造价师信息
+     * @return
+     */
+    List<WorkStaffCertificate> getFirstCostEngineerList();
+
+    /**
+     * 查询所有的一级造价师信息
+     * @return
+     */
+    Integer getSubmitApplicationsById(String id);
+
+    /**
+     * 对历史数据进行保存
+     * @param info
+     */
+    void insertHistory(WorkStaffCertificate info);
+}

+ 18 - 0
src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffBasicCertificationHistoryDao.java

@@ -0,0 +1,18 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.modules.workstaff.dao;
+
+import com.jeeplus.common.persistence.CrudDao;
+import com.jeeplus.common.persistence.annotation.MyBatisDao;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
+
+/**
+ * 执业资格证书DAO接口
+ * @author ssrh
+ * @version 2018-07-27
+ */
+@MyBatisDao
+public interface WorkStaffBasicCertificationHistoryDao extends CrudDao<WorkStaffCertificate> {
+
+}

+ 20 - 0
src/main/java/com/jeeplus/modules/workstaff/dao/WorkStaffCertificateDao.java

@@ -54,4 +54,24 @@ public interface WorkStaffCertificateDao extends CrudDao<WorkStaffCertificate> {
      * @return
      */
     Integer getSubmitApplicationsById(String id);
+
+    /**
+     * 查询所有的一级造价师信息
+     * @return
+     */
+    Integer getTemporaryCertificateValidityCountById(String id);
+
+    /**
+     * 修改职业资格证临时信息
+     * @param info
+     */
+    void updateTemporaryCertificateValidity(WorkStaffCertificate info);
+
+    /**
+     * 新增职业资格证临时信息
+     * @param info
+     */
+    void insertTemporaryCertificateValidity(WorkStaffCertificate info);
+
+    WorkStaffCertificate getTemporaryCertificateValidityById(String id);
 }

+ 72 - 0
src/main/java/com/jeeplus/modules/workstaff/entity/WorkStaffCertificate.java

@@ -40,6 +40,14 @@ public class WorkStaffCertificate extends DataEntity<WorkStaffCertificate> {
 	private Date validEndDate;		//文件可使用有效结束时间
 	private Date startDate;		//文件完整的有效开始时间
 	private Date endDate;		//文件完整的有效结束时间
+	private String userId;		//用户id
+	private String userName;		//用户id
+	private String temporaryUrl;		//文件临时路径
+	private String isAdmin;		//是否为管理员
+	private String possessUserId;		//证书所属人id
+	private String possessUserName;		//证书所属人名称
+	private String officeId;
+	private String officeName;
 
     public String getFileName() {
         return fileName;
@@ -237,4 +245,68 @@ public class WorkStaffCertificate extends DataEntity<WorkStaffCertificate> {
 	public void setStartDate(Date startDate) {
 		this.startDate = startDate;
 	}
+
+	public String getUserId() {
+		return userId;
+	}
+
+	public void setUserId(String userId) {
+		this.userId = userId;
+	}
+
+	public String getTemporaryUrl() {
+		return temporaryUrl;
+	}
+
+	public void setTemporaryUrl(String temporaryUrl) {
+		this.temporaryUrl = temporaryUrl;
+	}
+
+	public String getIsAdmin() {
+		return isAdmin;
+	}
+
+	public void setIsAdmin(String isAdmin) {
+		this.isAdmin = isAdmin;
+	}
+
+	public String getUserName() {
+		return userName;
+	}
+
+	public void setUserName(String userName) {
+		this.userName = userName;
+	}
+
+	public String getPossessUserId() {
+		return possessUserId;
+	}
+
+	public void setPossessUserId(String possessUserId) {
+		this.possessUserId = possessUserId;
+	}
+
+	public String getPossessUserName() {
+		return possessUserName;
+	}
+
+	public void setPossessUserName(String possessUserName) {
+		this.possessUserName = possessUserName;
+	}
+
+	public String getOfficeId() {
+		return officeId;
+	}
+
+	public void setOfficeId(String officeId) {
+		this.officeId = officeId;
+	}
+
+	public String getOfficeName() {
+		return officeName;
+	}
+
+	public void setOfficeName(String officeName) {
+		this.officeName = officeName;
+	}
 }

+ 85 - 0
src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicCertificationHistoryService.java

@@ -0,0 +1,85 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.modules.workstaff.service;
+
+import com.jeeplus.common.persistence.Page;
+import com.jeeplus.common.service.CrudService;
+import com.jeeplus.modules.sys.dao.UserDao;
+import com.jeeplus.modules.sys.entity.User;
+import com.jeeplus.modules.sys.service.WorkattachmentService;
+import com.jeeplus.modules.sys.utils.UserUtils;
+import com.jeeplus.modules.workstaff.dao.WorkStaffBasicCertificationHistoryDao;
+import com.jeeplus.modules.workstaff.dao.WorkStaffBasicInfoDao;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
+import com.jeeplus.modules.workstaffachiveslog.service.WorkStaffAchivesLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 执业资格证书Service
+ * @author ssrh
+ * @version 2018-07-27
+ */
+@Service
+@Transactional(readOnly = true)
+public class WorkStaffBasicCertificationHistoryService extends CrudService<WorkStaffBasicCertificationHistoryDao, WorkStaffCertificate> {
+    @Autowired
+    private WorkStaffAchivesLogService workStaffAchivesLogService;
+
+    @Autowired
+    private WorkStaffBasicInfoService workStaffBasicInfoService;
+
+    @Autowired
+    private UserDao userDao;
+    @Autowired
+    private WorkStaffBasicCertificationHistoryDao certificateDao;
+    @Autowired
+    private WorkStaffBasicInfoDao workStaffBasicInfoDao;
+
+    @Autowired
+    private WorkattachmentService workattachmentService;
+
+	public WorkStaffCertificate get(String id) {
+        WorkStaffCertificate workStaffCertificate = super.get(id);
+        /*List<MainDictDetail> certificateType = DictUtils.getMainDictList("certificate_type");
+        for (MainDictDetail type : certificateType) {
+            if(workStaffCertificate.getName().equals(type.getValue())){
+                workStaffCertificate.setName(type.getLabel());
+                break;
+            }
+
+        }*/
+
+        //获取对应执业资格证的专业
+        /*List<MainDictDetail> certificateMajor = workStaffBasicInfoService.getMainDictDetailByType(workStaffCertificate.getName());
+        if(StringUtils.isNotBlank(workStaffCertificate.getMajor())){
+            for (MainDictDetail mainDictDetail : certificateMajor) {
+                if(workStaffCertificate.getMajor().equals(mainDictDetail.getValue())){
+                    workStaffCertificate.setMajor(mainDictDetail.getLabel());
+                }
+            }
+        }*/
+
+        //对文件url进行处理
+        workattachmentService.attachmentManageByUrlOnStaffCertificate(workStaffCertificate);
+        return workStaffCertificate;
+	}
+	
+	public Page<WorkStaffCertificate> findPage(Page<WorkStaffCertificate> page, WorkStaffCertificate workStaffCertificate) {
+        User user = UserUtils.getUser();
+        if(user.isAdmin()){
+            workStaffCertificate.setIsAdmin("admin");
+        }else{
+            workStaffCertificate.setIsAdmin("user");
+        }
+        Page<WorkStaffCertificate> workStaffCertificatePage = super.findPage(page, workStaffCertificate);
+        List<WorkStaffCertificate> list = workStaffCertificatePage.getList();
+        // 处理附件url
+        workattachmentService.attachmentManageByUrlOnStaffCertificateList(list);
+		return workStaffCertificatePage;
+	}
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1766 - 0
src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicCertificationService.java


+ 11 - 0
src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffBasicInfoService.java

@@ -2338,4 +2338,15 @@ public class WorkStaffBasicInfoService extends CrudService<WorkStaffBasicInfoDao
         }
         UserUtils.pushIm(userIds, contentStr);
     }
+
+    /**
+     * 根据userId更新手签章路径
+     */
+    @Transactional(readOnly = false)
+    public void updateHandSignature(String userId, String handSignature) {
+        WorkStaffBasicInfo workStaffBasicInfo = new WorkStaffBasicInfo();
+        workStaffBasicInfo.setUserId(userId);
+        workStaffBasicInfo.setHandSignature(handSignature);
+        workStaffBasicInfoDao.updateHandSignatureByUserId(workStaffBasicInfo);
+    }
 }

+ 30 - 2
src/main/java/com/jeeplus/modules/workstaff/service/WorkStaffCertificateService.java

@@ -535,6 +535,13 @@ public class WorkStaffCertificateService extends CrudService<WorkStaffCertificat
                 entity.setUpdateDate(new Date());
                 dao.insert(entity);
             }else{
+                WorkStaffCertificate temporaryCertificateValidityById = certificateDao.getTemporaryCertificateValidityById(entity.getId());
+                if(null != temporaryCertificateValidityById){
+                    entity.setValidStartDate(temporaryCertificateValidityById.getValidStartDate());
+                    entity.setValidEndDate(temporaryCertificateValidityById.getValidEndDate());
+                    entity.setValidStartDate(temporaryCertificateValidityById.getValidStartDate());
+                    entity.setValidEndDate(temporaryCertificateValidityById.getValidEndDate());
+                }
                 this.save(entity);
             }
             if(entity!=null&&"1".equals(entity.getDelFlag())){
@@ -988,7 +995,18 @@ public class WorkStaffCertificateService extends CrudService<WorkStaffCertificat
                                 newCertificate.setEndDate(certValidEndDate);
 
                                 //修改证书的有效日期
-                                certificateDao.updateCertificateValidity(newCertificate);
+                                //certificateDao.updateCertificateValidity(newCertificate);
+                                Integer temporaryCertificateValidityById = certificateDao.getTemporaryCertificateValidityCountById(newCertificate.getId());
+                                if(temporaryCertificateValidityById > 0){
+                                    //修改证书的有效日期
+                                    newCertificate.preUpdate();
+                                    certificateDao.updateTemporaryCertificateValidity(newCertificate);
+                                }else{
+                                    String id = newCertificate.getId();
+                                    newCertificate.preInsert();
+                                    newCertificate.setId(id);
+                                    certificateDao.insertTemporaryCertificateValidity(newCertificate);
+                                }
                                 // -------------------------- 4. 条件判断:name=161 且 是PDF,调用disposeFile --------------------------
 
                             }else{
@@ -1241,7 +1259,17 @@ public class WorkStaffCertificateService extends CrudService<WorkStaffCertificat
                                 newCertificate.setEndDate(certValidEndDate);
 
                                 //修改证书的有效日期
-                                certificateDao.updateCertificateValidity(newCertificate);
+                                //certificateDao.updateCertificateValidity(newCertificate);
+                                //先检查临时表中是否存在记录,若存在则进行修改没否则进行新增
+                                Integer temporaryCertificateValidityById = certificateDao.getTemporaryCertificateValidityCountById(newCertificate.getId());
+                                if(temporaryCertificateValidityById > 0){
+                                    //修改证书的有效日期
+                                    newCertificate.preUpdate();
+                                    certificateDao.updateTemporaryCertificateValidity(newCertificate);
+                                }else{
+                                    newCertificate.preInsert();
+                                    certificateDao.insertTemporaryCertificateValidity(newCertificate);
+                                }
 
                                 // -------------------------- 4. 条件判断:name=161 且 是PDF,调用disposeFile --------------------------
 

+ 268 - 0
src/main/java/com/jeeplus/modules/workstaff/web/WorkStaffBasicCertificationController.java

@@ -0,0 +1,268 @@
+package com.jeeplus.modules.workstaff.web;
+
+import com.jeeplus.common.config.Global;
+import com.jeeplus.common.json.AjaxJson;
+import com.jeeplus.common.persistence.Page;
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.common.web.BaseController;
+import com.jeeplus.modules.sys.entity.MainDictDetail;
+import com.jeeplus.modules.sys.entity.User;
+import com.jeeplus.modules.sys.service.WorkattachmentService;
+import com.jeeplus.modules.sys.utils.DictUtils;
+import com.jeeplus.modules.sys.utils.UserUtils;
+import com.jeeplus.modules.workstaff.entity.WorkStaffBasicInfo;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
+import com.jeeplus.modules.workstaff.service.WorkStaffBasicCertificationService;
+import com.jeeplus.modules.workstaff.service.WorkStaffBasicInfoService;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.Logical;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.subject.Subject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 人员资质controller
+ * @auther 徐滕
+ * @create 2026-05-26-9:57
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/workstaff/workStaffBasicCertification")
+public class WorkStaffBasicCertificationController extends BaseController {
+
+    @Autowired
+    private WorkStaffBasicInfoService workStaffBasicInfoService;
+    @Autowired
+    private WorkStaffBasicCertificationService workStaffCertificateService;
+    @Autowired
+    private WorkattachmentService workattachmentService;
+
+    @ModelAttribute
+    public WorkStaffCertificate get(@RequestParam(required=false) String id) {
+        WorkStaffCertificate entity = null;
+        if (StringUtils.isNotBlank(id)){
+            entity = workStaffCertificateService.get(id);
+        }
+        if (entity == null){
+            entity = new WorkStaffCertificate();
+            entity.setId(id);
+        }
+        return entity;
+    }
+
+    @RequiresPermissions("workstaff:workStaffBasicCertification:list")
+    @RequestMapping(value = {"list", ""})
+    public String list(WorkStaffCertificate workStaffCertificate, HttpServletRequest request, HttpServletResponse response, Model model) {
+        //进行查询之后进行任何操作,返回还是查询之后的数据页面
+        if (StringUtils.isNotBlank(workStaffCertificate.getToflag())){
+            if (workStaffCertificate.getToflag().equals("1")){
+                request.getSession().removeAttribute("certification");
+                WorkStaffCertificate search=workStaffCertificate;
+                request.getSession().setAttribute("certification",search);
+            }
+        }else{
+            if (request.getSession().getAttribute("certification")!=null){
+                workStaffCertificate= (WorkStaffCertificate) request.getSession().getAttribute("certification");
+                model.addAttribute("workStaffCertificate", workStaffCertificate);
+            }
+        }
+        User user = UserUtils.getUser();
+        Subject subject = SecurityUtils.getSubject();
+        if(user.isAdmin()  || subject.isPermitted("workstaff:workStaffBasicCertification:viewAll")){
+            workStaffCertificate.setIsAdmin("admin");
+        }else{
+            workStaffCertificate.setIsAdmin("user");
+        }
+
+        workStaffCertificate.setUserId(user.getId());
+        Page<WorkStaffCertificate> page = workStaffCertificateService.findPage(new Page<WorkStaffCertificate>(request, response), workStaffCertificate);
+        List<WorkStaffCertificate> certificateList = page.getList();
+        List<MainDictDetail> certificateType = DictUtils.getMainDictList("certificate_type");
+
+        for (WorkStaffCertificate info: certificateList) {
+
+            //获取对应执业资格证的专业
+            List<MainDictDetail> certificateMajor = workStaffBasicInfoService.getMainDictDetailByType(info.getName());
+            if(StringUtils.isNotBlank(info.getMajor())){
+                for (MainDictDetail mainDictDetail : certificateMajor) {
+                    if(info.getMajor().equals(mainDictDetail.getValue())){
+                        info.setMajor(mainDictDetail.getLabel());
+                    }
+                }
+            }
+            //获取对应执业资格证类型
+            for (MainDictDetail type : certificateType) {
+                if(info.getName().equals(type.getValue())){
+                    info.setName(type.getLabel());
+                }
+            }
+        }
+
+        model.addAttribute("page", page);
+        return "modules/workstaff/cerification/workStaffBasicCertificationList";
+    }
+
+
+
+
+    /**
+     * 查看,增加,编辑员工信息表单页面
+     */
+    @RequiresPermissions(value={"workstaff:workStaffBasicCertification:add","workstaff:workStaffBasicCertification:edit"},logical= Logical.OR)
+    @RequestMapping(value = "form")
+    public String form(WorkStaffCertificate workStaffCertificate, Model model, boolean reFlag) {
+
+
+        model.addAttribute("workStaffCertificate", workStaffCertificate);
+        String view = "modules/workstaff/cerification/workStaffBasicCertificationForm";
+        return view;
+    }
+
+    /**
+     * 查看,增加,编辑员工信息表单页面
+     */
+    @RequiresPermissions(value={"workstaff:workStaffBasicCertification:view"},logical= Logical.OR)
+    @RequestMapping(value = "view")
+    public String view(WorkStaffCertificate workStaffCertificate, Model model, boolean reFlag) {
+
+        List<MainDictDetail> certificateType = DictUtils.getMainDictList("certificate_type");
+
+        //获取对应执业资格证的专业
+        List<MainDictDetail> certificateMajor = workStaffBasicInfoService.getMainDictDetailByType(workStaffCertificate.getName());
+        if(StringUtils.isNotBlank(workStaffCertificate.getMajor())){
+            for (MainDictDetail mainDictDetail : certificateMajor) {
+                if(workStaffCertificate.getMajor().equals(mainDictDetail.getValue())){
+                    workStaffCertificate.setMajor(mainDictDetail.getLabel());
+                }
+            }
+        }
+
+        for (MainDictDetail type : certificateType) {
+            if(workStaffCertificate.getName().equals(type.getValue())){
+                workStaffCertificate.setName(type.getLabel());
+            }
+        }
+
+
+
+        model.addAttribute("workStaffCertificate", workStaffCertificate);
+        String view = "modules/workstaff/cerification/workStaffBasicCertificationView";
+        return view;
+    }
+
+
+    /**
+     * 保存证书信息
+     */
+    @RequiresPermissions(value={"workstaff:workStaffBasicCertification:add","workstaff:workStaffBasicCertification:edit"},logical= Logical.OR)
+    @RequestMapping(value = "save")
+    public String save(WorkStaffCertificate workStaffCertificate, Model model, RedirectAttributes redirectAttributes) {
+        // 设置当前用户ID
+        workStaffCertificate.setUserId(UserUtils.getUser().getId());
+        workStaffCertificateService.saveInfo(workStaffCertificate);
+        addMessage(redirectAttributes, "保存证书信息成功");
+        return "redirect:" + Global.getAdminPath() + "/workstaff/workStaffBasicCertification/list?repage";
+    }
+
+    /**
+     * 手签章编辑页面
+     */
+    @RequiresPermissions("workstaff:workStaffBasicCertification:alterationSignature")
+    @RequestMapping(value = "handSignatureForm")
+    public String handSignatureForm(Model model) {
+        WorkStaffBasicInfo workStaffBasicInfo = workStaffBasicInfoService.getWorkStaffBasicInfoByUserId(UserUtils.getUser().getId());
+        if (workStaffBasicInfo == null) {
+            workStaffBasicInfo = new WorkStaffBasicInfo();
+        }
+        if(StringUtils.isNotBlank(workStaffBasicInfo.getHandSignature())){
+            String temporaryLookUrl = workattachmentService.fileTemporaryLookUrlManageOnPrefix(workStaffBasicInfo.getHandSignature());
+            workStaffBasicInfo.setHandSignatureUrl(temporaryLookUrl);
+        }
+        System.out.println(workStaffBasicInfo.getHandSignature());
+        System.out.println(workStaffBasicInfo.getHandSignatureUrl());
+        model.addAttribute("workStaffBasicInfo", workStaffBasicInfo);
+        return "modules/workstaff/cerification/handSignatureForm";
+    }
+
+    /**
+     * 保存手签章
+     */
+    @RequiresPermissions("workstaff:workStaffBasicCertification:alterationSignature")
+    @RequestMapping(value = "saveHandSignature")
+    public String saveHandSignature(WorkStaffBasicInfo workStaffBasicInfo, Model model, RedirectAttributes redirectAttributes) {
+        String userId = UserUtils.getUser().getId();
+        String handSignature = workStaffBasicInfo.getHandSignature();
+        if (StringUtils.isNotBlank(handSignature)) {
+            // 存储相对路径,去掉阿里云前缀
+            String aliyunUrl = Global.getConfig("aliyunUrl");
+            if (StringUtils.isNotBlank(aliyunUrl) && handSignature.contains(aliyunUrl)) {
+                handSignature = handSignature.replace(aliyunUrl, "");
+            }
+            workStaffBasicInfoService.updateHandSignature(userId, handSignature);
+            addMessage(redirectAttributes, "手签章变更成功");
+        } else {
+            addMessage(redirectAttributes, "请上传手签章图片");
+        }
+        return "redirect:" + Global.getAdminPath() + "/workstaff/workStaffBasicCertification/list?repage";
+    }
+
+    /**
+     * 检查当前用户是否已存在相同证书名称的记录
+     */
+    @ResponseBody
+    @RequestMapping(value = "checkCertificateName")
+    public AjaxJson checkCertificateName(@RequestParam String name, @RequestParam(required = false) String id) {
+        AjaxJson j = new AjaxJson();
+        String userId = UserUtils.getUser().getId();
+        WorkStaffCertificate query = new WorkStaffCertificate();
+        query.setUserId(userId);
+        List<WorkStaffCertificate> list = workStaffCertificateService.findList(query);
+        boolean exists = false;
+        for (WorkStaffCertificate cert : list) {
+            if (name.equals(cert.getName())) {
+                // 编辑模式下排除自身记录
+                if (StringUtils.isNotBlank(id) && id.equals(cert.getId())) {
+                    continue;
+                }
+                exists = true;
+                break;
+            }
+        }
+        if (exists) {
+            j.setSuccess(false);
+            j.setMsg("该证书名称已存在,不可重复添加,请返回修改!");
+        } else {
+            j.setSuccess(true);
+        }
+        return j;
+    }
+
+    /**
+     * 检查当前用户是否已上传手签章
+     */
+    @ResponseBody
+    @RequestMapping(value = "checkHandSignature")
+    public AjaxJson checkHandSignature() {
+        AjaxJson j = new AjaxJson();
+        String userId = UserUtils.getUser().getId();
+        WorkStaffBasicInfo basicInfo = workStaffBasicInfoService.getWorkStaffBasicInfoByUserId(userId);
+        if (basicInfo != null && StringUtils.isNotBlank(basicInfo.getHandSignature())) {
+            j.setSuccess(true);
+        } else {
+            j.setSuccess(false);
+            j.setMsg("您尚未上传手签章,请先上传手签章后再进行操作!");
+        }
+        return j;
+    }
+
+}

+ 111 - 0
src/main/java/com/jeeplus/modules/workstaff/web/WorkStaffBasicCertificationHistoryController.java

@@ -0,0 +1,111 @@
+package com.jeeplus.modules.workstaff.web;
+
+import com.jeeplus.common.persistence.Page;
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.common.web.BaseController;
+import com.jeeplus.modules.sys.entity.MainDictDetail;
+import com.jeeplus.modules.sys.service.WorkattachmentService;
+import com.jeeplus.modules.sys.utils.DictUtils;
+import com.jeeplus.modules.sys.utils.UserUtils;
+import com.jeeplus.modules.workstaff.entity.WorkStaffCertificate;
+import com.jeeplus.modules.workstaff.service.WorkStaffBasicCertificationHistoryService;
+import com.jeeplus.modules.workstaff.service.WorkStaffBasicInfoService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 人员资质controller
+ * @auther 徐滕
+ * @create 2026-05-26-9:57
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/workstaff/workStaffBasicCertificationHistory")
+public class WorkStaffBasicCertificationHistoryController extends BaseController {
+
+    @Autowired
+    private WorkStaffBasicInfoService workStaffBasicInfoService;
+    @Autowired
+    private WorkStaffBasicCertificationHistoryService workStaffCertificateService;
+    @Autowired
+    private WorkattachmentService workattachmentService;
+
+    @ModelAttribute
+    public WorkStaffCertificate get(@RequestParam(required=false) String id) {
+        WorkStaffCertificate entity = null;
+        if (StringUtils.isNotBlank(id)){
+            entity = workStaffCertificateService.get(id);
+        }
+        if (entity == null){
+            entity = new WorkStaffCertificate();
+            entity.setId(id);
+        }
+        return entity;
+    }
+
+    @RequiresPermissions("workstaff:workStaffBasicCertification:list")
+    @RequestMapping(value = {"list", ""})
+    public String list(WorkStaffCertificate workStaffCertificate, HttpServletRequest request, HttpServletResponse response, Model model) {
+        //进行查询之后进行任何操作,返回还是查询之后的数据页面
+        if (StringUtils.isNotBlank(workStaffCertificate.getToflag())){
+            if (workStaffCertificate.getToflag().equals("1")){
+                request.getSession().removeAttribute("certification");
+                WorkStaffCertificate search=workStaffCertificate;
+                request.getSession().setAttribute("certification",search);
+            }
+        }else{
+            if (request.getSession().getAttribute("certification")!=null){
+                workStaffCertificate= (WorkStaffCertificate) request.getSession().getAttribute("certification");
+                model.addAttribute("workStaffCertificate", workStaffCertificate);
+            }
+        }
+        workStaffCertificate.setUserId(UserUtils.getUser().getId());
+        Page<WorkStaffCertificate> page = workStaffCertificateService.findPage(new Page<WorkStaffCertificate>(request, response), workStaffCertificate);
+        List<WorkStaffCertificate> certificateList = page.getList();
+        List<MainDictDetail> certificateType = DictUtils.getMainDictList("certificate_type");
+        for (MainDictDetail type : certificateType) {
+            for (WorkStaffCertificate info: certificateList) {
+                System.out.println(info.getName());
+                if(info.getName().equals(type.getValue())){
+                    info.setName(type.getLabel());
+                }
+
+                //获取对应执业资格证的专业
+                List<MainDictDetail> certificateMajor = workStaffBasicInfoService.getMainDictDetailByType(info.getName());
+                if(StringUtils.isNotBlank(info.getMajor())){
+                    for (MainDictDetail mainDictDetail : certificateMajor) {
+                        if(info.getMajor().equals(mainDictDetail.getValue())){
+                            info.setMajor(mainDictDetail.getLabel());
+                        }
+                    }
+                }
+            }
+        }
+
+        model.addAttribute("page", page);
+        return "modules/workstaff/cerification/workStaffBasicCertificationHistoryList";
+    }
+
+
+
+
+    /**
+     * 查看员工信息表单页面
+     */
+    @RequestMapping(value = "view")
+    public String view(WorkStaffCertificate workStaffCertificate, Model model, boolean reFlag) {
+        model.addAttribute("workStaffCertificate", workStaffCertificate);
+        String view = "modules/workstaff/cerification/workStaffBasicCertificationView";
+        return view;
+    }
+
+
+}

+ 393 - 0
src/main/resources/mappings/modules/workstaff/WorkStaffBasicCertificationDao.xml

@@ -0,0 +1,393 @@
+<?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.jeeplus.modules.workstaff.dao.WorkStaffBasicCertificationDao">
+    
+	<sql id="workStaffCertificateColumns">
+		a.id AS "id",
+		a.create_by AS "createBy.id",
+		a.create_date AS "createDate",
+		a.update_by AS "updateBy.id",
+		a.update_date AS "updateDate",
+		a.remarks AS "remarks",
+		a.del_flag AS "delFlag",
+		a.staff_id AS "staffId",
+		a.name AS "name",
+		a.num AS "num",
+		a.issuing_authority AS "issuingAuthority",
+		a.issuing_date AS "issuingDate",
+		a.regist_date AS "registDate",
+		a.regist_num AS "registNum",
+		a.major AS "major",
+		a.grade AS "grade",
+		a.iss_type AS "issType",
+		a.file_path AS "filePath",
+		a.file_path AS "fileUrl",
+		a.file_name AS "fileName",
+		a.valid_start_date AS "validStartDate",
+		a.valid_end_date AS "validEndDate",
+		a.start_date AS "startDate",
+		a.end_date As "endDate"
+	</sql>
+	
+	<sql id="workStaffCertificateJoins">
+		left join work_staff_achives b ON a.staff_id = b.id
+		left join sys_user c ON b.user_id = c.id
+		LEFT JOIN sys_office o ON c.office_id = o.id          <!-- 部门 -->
+	</sql>
+	
+    
+	<select id="get" resultType="WorkStaffCertificate" >
+		SELECT 
+			<include refid="workStaffCertificateColumns"/>
+			,c.id AS "userId"
+		    ,c.name AS "userName"
+			,o.name AS "officeName"
+		FROM work_staff_certificate a
+		<include refid="workStaffCertificateJoins"/>
+		WHERE a.id = #{id}
+	</select>
+	
+	<select id="findList" resultType="WorkStaffCertificate" >
+		SELECT 
+			<include refid="workStaffCertificateColumns"/>
+			,c.id AS "userId"
+			,c.name AS "userName"
+			,o.name AS "officeName"
+		FROM work_staff_certificate a
+		<include refid="workStaffCertificateJoins"/>
+		<where>
+			a.del_flag = #{DEL_FLAG_NORMAL}
+			<if test="isAdmin != null and isAdmin != 'admin'">
+				AND a.staff_id = (select id from work_staff_achives where user_id = #{userId})
+			</if>
+			<if test="name != null and name != ''">
+				AND a.name LIKE 
+					<if test="dbName == 'oracle'">'%'||#{name}||'%'</if>
+					<if test="dbName == 'mssql'">'%'+#{name}+'%'</if>
+					<if test="dbName == 'mysql'">concat('%',#{name},'%')</if>
+			</if>
+			<!-- 时间区间交集查询 -->
+			<if test="validStartDate != null and validStartDate != ''">
+				AND a.valid_end_date &gt;= #{validStartDate}
+			</if>
+			<if test="validEndDate != null and validEndDate != ''">
+				AND a.valid_start_date &lt;= #{validEndDate}
+			</if>
+
+
+			<!-- ====================== 人员筛选:ID 或 姓名(OR 关系) ====================== -->
+			<if test="(possessUserId != null and possessUserId != '') or (possessUserName != null and possessUserName != '')">
+				AND (
+				<if test="possessUserId != null and possessUserId != ''">
+					c.id = #{possessUserId}
+				</if>
+				<if test="possessUserId != null and possessUserId != '' and possessUserName != null and possessUserName != ''">
+					OR
+				</if>
+				<if test="possessUserName != null and possessUserName != ''">
+					c.name LIKE concat('%',#{possessUserName},'%')
+				</if>
+				)
+			</if>
+
+			<!-- ====================== 部门筛选 ====================== -->
+			<if test="officeIdList!=null and officeIdList.size!=0">
+				AND c.office_id IN
+				<foreach collection="officeIdList" item="officeId" separator="," open="(" close=")">
+					#{officeId}
+				</foreach>
+			</if>
+
+		</where>
+		<choose>
+			<when test="page !=null and page.orderBy != null and page.orderBy != ''">
+				ORDER BY ${page.orderBy}
+			</when>
+			<otherwise>
+				ORDER BY a.update_date desc
+			</otherwise>
+		</choose>
+	</select>
+	
+	<select id="findAllList" resultType="WorkStaffCertificate" >
+		SELECT 
+			<include refid="workStaffCertificateColumns"/>
+		FROM work_staff_certificate a
+		<include refid="workStaffCertificateJoins"/>
+		<where>
+			a.del_flag = #{DEL_FLAG_NORMAL}
+		</where>		
+		<choose>
+			<when test="page !=null and page.orderBy != null and page.orderBy != ''">
+				ORDER BY ${page.orderBy}
+			</when>
+			<otherwise>
+				ORDER BY a.update_date DESC
+			</otherwise>
+		</choose>
+	</select>
+	
+	<insert id="insert">
+		INSERT INTO work_staff_certificate(
+			id,
+			create_by,
+			create_date,
+			update_by,
+			update_date,
+			remarks,
+			del_flag,
+			staff_id,
+			name,
+			num,
+			issuing_authority,
+			issuing_date,
+			regist_date,
+			regist_num,
+			major,
+			grade,
+			iss_type,
+			file_path,
+			file_name,
+			valid_start_date,
+			valid_end_date,
+			start_date,
+			end_date
+		) VALUES (
+			#{id},
+			#{createBy.id},
+			#{createDate},
+			#{updateBy.id},
+			#{updateDate},
+			#{remarks},
+			#{delFlag},
+			#{staffId},
+			#{name},
+			#{num},
+			#{issuingAuthority},
+			#{issuingDate},
+			#{registDate},
+			#{registNum},
+			#{major},
+			#{grade},
+			#{issType},
+			#{filePath},
+			#{fileName},
+			#{validStartDate},
+			#{validEndDate},
+			#{startDate},
+			#{endDate}
+		)
+	</insert>
+
+	<insert id="insertHistory">
+		INSERT INTO work_staff_certificate_history(
+			id,
+			create_by,
+			create_date,
+			update_by,
+			update_date,
+			remarks,
+			del_flag,
+			staff_id,
+			name,
+			num,
+			issuing_authority,
+			issuing_date,
+			regist_date,
+			regist_num,
+			major,
+			grade,
+			iss_type,
+			file_path,
+			file_name,
+			valid_start_date,
+			valid_end_date,
+			start_date,
+			end_date
+		) VALUES (
+			#{id},
+			#{createBy.id},
+			#{createDate},
+			#{updateBy.id},
+			#{updateDate},
+			#{remarks},
+			#{delFlag},
+			#{staffId},
+			#{name},
+			#{num},
+			#{issuingAuthority},
+			#{issuingDate},
+			#{registDate},
+			#{registNum},
+			#{major},
+			#{grade},
+			#{issType},
+			#{filePath},
+			#{fileName},
+			#{validStartDate},
+			#{validEndDate},
+			#{startDate},
+			#{endDate}
+		)
+	</insert>
+	
+	<update id="update">
+		UPDATE work_staff_certificate SET 	
+			update_by = #{updateBy.id},
+			update_date = #{updateDate},
+			remarks = #{remarks},
+			staff_id = #{staffId},
+			name = #{name},
+			num = #{num},
+			issuing_authority = #{issuingAuthority},
+			issuing_date = #{issuingDate},
+			regist_date = #{registDate},
+			regist_num = #{registNum},
+			major = #{major},
+			grade = #{grade},
+			iss_type = #{issType},
+			file_path = #{filePath},
+			file_name = #{fileName},
+			<if test="validStartDate !=null">
+				valid_start_date = #{validStartDate},
+			</if>
+			<if test="validEndDate !=null">
+				valid_end_date = #{validEndDate},
+			</if>
+			<if test="startDate !=null">
+				start_date = #{startDate},
+			</if>
+			end_date = #{endDate}
+		WHERE id = #{id}
+	</update>
+
+	<update id="updateCertificateValidity">
+		UPDATE work_staff_certificate SET
+			<if test="validStartDate !=null">
+				valid_start_date = #{validStartDate},
+			</if>
+			<if test="validEndDate !=null">
+				valid_end_date = #{validEndDate},
+			</if>
+			<if test="startDate !=null">
+				start_date = #{startDate},
+			</if>
+			end_date = #{endDate}
+		WHERE id = #{id}
+	</update>
+	
+	
+	<!--物理删除-->
+	<update id="delete">
+		DELETE FROM work_staff_certificate
+		WHERE id = #{id}
+	</update>
+	
+	<!--逻辑删除-->
+	<update id="deleteByLogic">
+		UPDATE work_staff_certificate SET
+			update_by = #{updateBy.id},
+			update_date = #{updateDate},
+			del_flag = #{DEL_FLAG_DELETE}
+		WHERE id = #{id}
+	</update>
+	
+	
+	<!-- 根据实体名称和字段名称和字段值获取唯一记录 -->
+	<select id="findUniqueByProperty" resultType="WorkStaffCertificate" statementType="STATEMENT">
+		select * FROM work_staff_certificate  where ${propertyName} = '${value}'
+	</select>
+	<update id="updateFalt" parameterType="java.lang.String">
+		UPDATE work_staff_certificate SET
+			del_flag = '1'
+		WHERE id in (select l.son_id from work_staff_achives_log l
+		where l.staff_id=#{id} and l.module='执业资格证书' and l.type='删除')
+	</update>
+
+	<select id="getByUserId" resultType="WorkStaffCertificate" >
+		SELECT
+		<include refid="workStaffCertificateColumns"/>
+		FROM work_staff_certificate a
+		<include refid="workStaffCertificateJoins"/>
+		<where>
+			a.del_flag = 0 and a.staff_id = #{userId}
+		</where>
+	</select>
+
+	<insert id="saveImport">
+		INSERT INTO work_staff_certificate(
+			id,
+			create_by,
+			create_date,
+			update_by,
+			update_date,
+			remarks,
+			del_flag,
+			staff_id,
+			name,
+			num,
+			issuing_authority,
+			issuing_date,
+			regist_date,
+			regist_num,
+			major,
+			file_name
+		) VALUES (
+			#{id},
+			#{createBy.id},
+			#{createDate},
+			#{updateBy.id},
+			#{updateDate},
+			#{remarks},
+			#{delFlag},
+			#{staffId},
+			#{name},
+			#{num},
+			#{issuingAuthority},
+			#{issuingDate},
+			#{registDate},
+			#{registNum},
+			#{major},
+			#{fileName}
+		)
+	</insert>
+	<update id="updateImport">
+		UPDATE work_staff_certificate SET
+			update_by = #{updateBy.id},
+			update_date = #{updateDate},
+			remarks = #{remarks},
+			staff_id = #{staffId},
+			name = #{name},
+			num = #{num},
+			issuing_authority = #{issuingAuthority},
+			issuing_date = #{issuingDate},
+			regist_date = #{registDate},
+			regist_num = #{registNum},
+			major = #{major},
+			file_name = #{fileName}
+		WHERE id = #{id}
+	</update>
+
+
+
+	<select id="getFirstCostEngineerList" resultType="WorkStaffCertificate" >
+		SELECT
+		<include refid="workStaffCertificateColumns"/>
+		FROM work_staff_certificate a
+		<include refid="workStaffCertificateJoins"/>
+		<where>
+			a.del_flag = 0 and name = '161'
+		</where>
+	</select>
+
+	<select id="getSubmitApplicationsById" resultType="java.lang.Integer" >
+		SELECT
+		count(1)
+		FROM work_staff_achives_log a
+		<where>
+			a.del_flag = 0 and son_id = #{id} and state = 1
+		</where>
+	</select>
+
+
+</mapper>

+ 108 - 0
src/main/resources/mappings/modules/workstaff/WorkStaffBasicCertificationHistoryDao.xml

@@ -0,0 +1,108 @@
+<?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.jeeplus.modules.workstaff.dao.WorkStaffBasicCertificationHistoryDao">
+    
+	<sql id="workStaffCertificateColumns">
+		a.id AS "id",
+		a.create_by AS "createBy.id",
+		a.create_date AS "createDate",
+		a.update_by AS "updateBy.id",
+		a.update_date AS "updateDate",
+		a.remarks AS "remarks",
+		a.del_flag AS "delFlag",
+		a.staff_id AS "staffId",
+		a.name AS "name",
+		a.num AS "num",
+		a.issuing_authority AS "issuingAuthority",
+		a.issuing_date AS "issuingDate",
+		a.regist_date AS "registDate",
+		a.regist_num AS "registNum",
+		a.major AS "major",
+		a.grade AS "grade",
+		a.iss_type AS "issType",
+		a.file_path AS "filePath",
+		a.file_path AS "fileUrl",
+		a.file_name AS "fileName",
+		a.valid_start_date AS "validStartDate",
+		a.valid_end_date AS "validEndDate",
+		a.start_date AS "startDate",
+		a.end_date As "endDate"
+	</sql>
+
+
+	<sql id="workStaffCertificateJoins">
+		left join work_staff_achives b ON a.staff_id = b.id
+		left join sys_user c ON b.user_id = c.id
+		LEFT JOIN sys_office o ON c.office_id = o.id          <!-- 部门 -->
+	</sql>
+	
+    
+	<select id="get" resultType="WorkStaffCertificate" >
+		SELECT 
+			<include refid="workStaffCertificateColumns"/>
+		,c.name AS "userName"
+		,o.name AS "officeName"
+		FROM work_staff_certificate_history a
+		<include refid="workStaffCertificateJoins"/>
+		WHERE a.id = #{id}
+	</select>
+	
+	<select id="findList" resultType="WorkStaffCertificate" >
+		SELECT 
+			<include refid="workStaffCertificateColumns"/>
+		,c.name AS "userName"
+		,o.name AS "officeName"
+		FROM work_staff_certificate_history a
+		<include refid="workStaffCertificateJoins"/>
+		<where>
+			a.del_flag = #{DEL_FLAG_NORMAL}
+			<if test="isAdmin != null and isAdmin != 'admin'">
+				AND a.staff_id = (select id from work_staff_achives where user_id = #{userId})
+			</if>
+			<if test="name != null and name != ''">
+				AND a.name LIKE 
+					<if test="dbName == 'oracle'">'%'||#{name}||'%'</if>
+					<if test="dbName == 'mssql'">'%'+#{name}+'%'</if>
+					<if test="dbName == 'mysql'">concat('%',#{name},'%')</if>
+			</if>
+			<!-- 时间区间交集查询 -->
+			<if test="validStartDate != null and validStartDate != ''">
+				AND a.valid_end_date &gt;= #{validStartDate}
+			</if>
+			<if test="validEndDate != null and validEndDate != ''">
+				AND a.valid_start_date &lt;= #{validEndDate}
+			</if>
+
+			<!-- ====================== 人员筛选:ID 或 姓名(OR 关系) ====================== -->
+			<if test="(possessUserId != null and possessUserId != '') or (possessUserName != null and possessUserName != '')">
+				AND (
+				<if test="possessUserId != null and possessUserId != ''">
+					c.id = #{possessUserId}
+				</if>
+				<if test="possessUserId != null and possessUserId != '' and possessUserName != null and possessUserName != ''">
+					OR
+				</if>
+				<if test="possessUserName != null and possessUserName != ''">
+					c.name LIKE concat('%',#{possessUserName},'%')
+				</if>
+				)
+			</if>
+
+			<!-- ====================== 部门筛选 ====================== -->
+			<if test="officeIdList!=null and officeIdList.size!=0">
+				AND c.office_id IN
+				<foreach collection="officeIdList" item="officeId" separator="," open="(" close=")">
+					#{officeId}
+				</foreach>
+			</if>
+		</where>
+		<choose>
+			<when test="page !=null and page.orderBy != null and page.orderBy != ''">
+				ORDER BY ${page.orderBy}
+			</when>
+			<otherwise>
+				ORDER BY a.update_date desc
+			</otherwise>
+		</choose>
+	</select>
+</mapper>

+ 87 - 0
src/main/resources/mappings/modules/workstaff/WorkStaffCertificateDao.xml

@@ -295,5 +295,92 @@
 		</where>
 	</select>
 
+	<select id="getTemporaryCertificateValidityCountById" resultType="java.lang.Integer" >
+		SELECT
+		count(1)
+		FROM work_staff_certificate_temporary a
+		<where>
+			a.del_flag = 0 and id = #{id}
+		</where>
+	</select>
+
+
+	<update id="updateTemporaryCertificateValidity">
+		UPDATE work_staff_certificate_temporary SET
+		update_by = #{updateBy.id},
+		update_date = #{updateDate},
+		remarks = #{remarks},
+		staff_id = #{staffId},
+		name = #{name},
+		<if test="validStartDate !=null">
+			valid_start_date = #{validStartDate},
+		</if>
+		<if test="validEndDate !=null">
+			valid_end_date = #{validEndDate},
+		</if>
+		<if test="startDate !=null">
+			start_date = #{startDate},
+		</if>
+		end_date = #{endDate}
+		WHERE id = #{id}
+	</update>
+
+	<insert id="insertTemporaryCertificateValidity">
+		INSERT INTO work_staff_certificate_temporary(
+			id,
+			create_by,
+			create_date,
+			update_by,
+			update_date,
+			remarks,
+			del_flag,
+			staff_id,
+			name,
+
+			valid_start_date,
+			valid_end_date,
+			start_date,
+			end_date
+		) VALUES (
+			 #{id},
+			 #{createBy.id},
+			 #{createDate},
+			 #{updateBy.id},
+			 #{updateDate},
+			 #{remarks},
+			 #{delFlag},
+			 #{staffId},
+			 #{name},
+
+			 #{validStartDate},
+			 #{validEndDate},
+			 #{startDate},
+			 #{endDate}
+		 )
+	</insert>
+
+
+
+	<select id="getTemporaryCertificateValidityById" resultType="WorkStaffCertificate" >
+		SELECT
+		a.id AS "id",
+		a.create_by AS "createBy.id",
+		a.create_date AS "createDate",
+		a.update_by AS "updateBy.id",
+		a.update_date AS "updateDate",
+		a.remarks AS "remarks",
+		a.del_flag AS "delFlag",
+		a.staff_id AS "staffId",
+		a.name AS "name",
+		a.valid_start_date AS "validStartDate",
+		a.valid_end_date AS "validEndDate",
+		a.start_date AS "startDate",
+		a.end_date As "endDate"
+		FROM work_staff_certificate_temporary a
+		<where>
+			a.del_flag = 0 and id = #{id} limit 1
+		</where>
+	</select>
+
 
 </mapper>

+ 214 - 0
src/main/webapp/webpage/modules/workstaff/cerification/handSignatureForm.jsp

@@ -0,0 +1,214 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/webpage/include/taglib.jsp"%>
+<html>
+<head>
+    <title>变更手签章</title>
+    <meta name="decorator" content="default"/>
+    <script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+    <link rel='stylesheet' type="text/css" href="${ctxStatic}/layui/css/layui.css"/>
+    <style>
+        body {
+            padding: 20px;
+            background: #f8f8f8;
+        }
+        .signature-container {
+            background: #fff;
+            border-radius: 8px;
+            padding: 30px;
+            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
+            max-width: 500px;
+            margin: 0 auto;
+        }
+        .signature-title {
+            font-size: 16px;
+            color: #333;
+            margin-bottom: 8px;
+            font-weight: bold;
+        }
+        .signature-tip {
+            color: #ff5722;
+            font-size: 13px;
+            margin-bottom: 20px;
+            line-height: 1.6;
+        }
+        .signature-preview-area {
+            text-align: center;
+            padding: 20px;
+            border: 2px dashed #e0e0e0;
+            border-radius: 6px;
+            min-height: 180px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            flex-direction: column;
+            margin-bottom: 20px;
+            background: #fafafa;
+            transition: border-color 0.3s;
+        }
+        .signature-preview-area:hover {
+            border-color: #1E9FFF;
+        }
+        .signature-preview-area img {
+            max-width: 300px;
+            max-height: 200px;
+            cursor: pointer;
+            border-radius: 4px;
+            box-shadow: 0 2px 6px rgba(0,0,0,0.1);
+        }
+        .signature-preview-area .no-signature {
+            color: #999;
+            font-size: 14px;
+        }
+        .signature-preview-area .no-signature i {
+            font-size: 48px;
+            display: block;
+            margin-bottom: 10px;
+            color: #ccc;
+        }
+        .upload-btn-group {
+            text-align: center;
+            margin-top: 15px;
+        }
+        .upload-btn-group .layui-btn {
+            min-width: 120px;
+        }
+        .upload-status {
+            text-align: center;
+            margin-top: 10px;
+            font-size: 13px;
+        }
+        .signature-hint {
+            text-align: center;
+            color: #999;
+            font-size: 12px;
+            margin-top: 8px;
+        }
+    </style>
+    <script type="text/javascript">
+        var validateForm;
+        function doSubmit(obj){
+            var handSignature = $("#handSignature").val();
+            if (!handSignature) {
+                parent.layer.msg("请上传手签章图片!", {icon: 2});
+                return false;
+            }
+            $("#inputForm").submit();
+            return true;
+        }
+
+        $(document).ready(function() {
+            layui.use(['upload', 'layer'], function(){
+                var upload = layui.upload;
+                var layer = layui.layer;
+                var $ = layui.$;
+
+                // 手签章图片上传
+                var uploadInst = upload.render({
+                    elem: '#uploadSignatureBtn'
+                    ,url: '${ctx}/bos/upLoadHandSignatureImg'
+                    ,accept: 'images'
+                    ,acceptMime: 'image/jpeg,image/png,image/bmp,image/gif'
+                    ,size: 10240  // 10MB
+                    ,before: function(obj){
+                        obj.preview(function(index, file, result){
+                            $('#signatureImg').attr('src', result).show();
+                            $('#noSignatureTip').hide();
+                        });
+                        layer.msg('上传中...', {icon: 16, time: 0});
+                    }
+                    ,done: function(res){
+                        if(res.code != 1){
+                            return layer.msg('上传失败,请重试', {icon: 2});
+                        }
+                        $("#handSignature").val(res.url);
+                        layer.msg('上传成功', {icon: 1});
+                        $('#uploadStatus').html('<span style="color:#5FB878;"><i class="fa fa-check-circle"></i> 已上传新图片</span>');
+                        // 显示预览图及提示
+                        $('#signatureImg').show();
+                        $('#noSignatureTip').hide();
+                        if ($('#previewHint').length) { $('#previewHint').show(); }
+                    }
+                    ,error: function(){
+                        $('#uploadStatus').html('<span style="color:#FF5722;"><i class="fa fa-times-circle"></i> 上传失败</span>');
+                    }
+                });
+            });
+
+            validateForm = $("#inputForm").validate({
+                submitHandler: function(form){
+                    loading('正在提交,请稍等...');
+                    form.submit();
+                },
+                errorContainer: "#messageBox",
+                errorPlacement: function(error, element) {
+                    $("#messageBox").text("输入有误,请先更正。");
+                    error.insertAfter(element);
+                }
+            });
+        });
+
+        // 点击图片放大预览
+        function previewSignature(url) {
+            if (!url) return;
+            top.layer.open({
+                type: 1,
+                area: ['80%', '80%'],
+                title: '手签章预览',
+                shadeClose: true,
+                shade: 0.3,
+                content: '<div style="text-align:center;padding:20px;"><img src="' + url + '" style="max-width:100%;max-height:100%;"/></div>'
+            });
+        }
+    </script>
+</head>
+<body>
+<div class="signature-container">
+    <form:form id="inputForm" modelAttribute="workStaffBasicInfo" action="${ctx}/workstaff/workStaffBasicCertification/saveHandSignature" method="post" class="layui-form">
+
+        <div class="signature-title">手签章管理</div>
+        <div class="signature-tip">
+            <i class="fa fa-info-circle"></i>
+            请在白纸上签字并扫描,截取签字部分进行上传。支持 JPG、PNG、BMP 格式,大小不超过 10MB。
+        </div>
+
+        <div class="signature-preview-area">
+            <c:choose>
+                <c:when test="${not empty workStaffBasicInfo.handSignatureUrl}">
+                    <img id="signatureImg"
+                         src="${workStaffBasicInfo.handSignatureUrl}"
+                         onclick="previewSignature('${workStaffBasicInfo.handSignatureUrl}')"
+                         title="点击放大预览"
+                         alt="手签章"/>
+                    <div class="signature-hint">点击图片可放大预览</div>
+                </c:when>
+                <c:otherwise>
+                    <div id="noSignatureTip" class="no-signature">
+                        <i class="fa fa-cloud-upload"></i>
+                        <span>暂无手签章,请上传</span>
+                    </div>
+                    <img id="signatureImg" style="display:none;"
+                         onclick="previewSignature(this.src)"
+                         title="点击放大预览"
+                         alt="手签章"/>
+                    <div class="signature-hint" id="previewHint" style="display:none;">点击图片可放大预览</div>
+                </c:otherwise>
+            </c:choose>
+        </div>
+
+        <input type="hidden" id="handSignature" name="handSignature"
+               value="${not empty workStaffBasicInfo.handSignatureUrl ? workStaffBasicInfo.handSignatureUrl : ''}"/>
+
+        <div class="upload-btn-group">
+            <button type="button" class="layui-btn layui-btn-normal" id="uploadSignatureBtn">
+                <i class="fa fa-upload"></i>&nbsp;
+                <c:choose>
+                    <c:when test="${not empty workStaffBasicInfo.handSignatureUrl}">变更手签章</c:when>
+                    <c:otherwise>上传手签章</c:otherwise>
+                </c:choose>
+            </button>
+        </div>
+        <div class="upload-status" id="uploadStatus"></div>
+    </form:form>
+</div>
+</body>
+</html>

+ 570 - 0
src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationForm.jsp

@@ -0,0 +1,570 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/webpage/include/taglib.jsp"%>
+<html>
+<head>
+    <title>发票管理</title>
+    <meta name="decorator" content="default"/>
+    <script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+    <link rel='stylesheet' type="text/css" href="${ctxStatic}/layui/css/layui.css"/>
+    <script src="${ctxStatic}/common/html/js/script.js"></script>
+    <style>
+        #contractTypeDoc-error{
+            top:80px;
+            left:0;
+        }
+        /*超过5个汉字,调整label的长度,以下是配套的*/
+        .layui-item .layui-form-label{
+            width:90px;
+        }
+        .form-group .layui-item .layui-input-block,
+        .query .layui-input-block {
+            margin-left: 116px;
+        }
+        #workInvoiceProjectRelationList td{
+            padding-left: 0px;
+            padding-right: 0px;
+        }
+        .file-upload-section {
+            padding: 10px 0;
+        }
+        .file-upload-section .upload-btn {
+            display: inline-block;
+            padding: 5px 15px;
+            background-color: #1E9FFF;
+            color: #fff;
+            border-radius: 3px;
+            cursor: pointer;
+            font-size: 13px;
+        }
+        .file-upload-section .upload-btn:hover {
+            background-color: #0d8de6;
+        }
+        .file-preview-area {
+            display: inline-block;
+            vertical-align: middle;
+            margin-left: 10px;
+        }
+        .file-preview-area img {
+            vertical-align: middle;
+            border: 1px solid #e6e6e6;
+            border-radius: 3px;
+        }
+        .file-preview-area .file-link {
+            color: #007bff;
+            text-decoration: none;
+            margin-right: 8px;
+        }
+        .file-preview-area .file-link:hover {
+            text-decoration: underline;
+        }
+        .file-preview-area .remove-file-btn {
+            color: #ff5722;
+            cursor: pointer;
+            margin-left: 5px;
+            text-decoration: none;
+        }
+        .file-preview-area .remove-file-btn:hover {
+            text-decoration: underline;
+        }
+    </style>
+    <script type="text/javascript">
+        var validateForm;
+        function doSubmit(obj){//回调函数,在编辑和保存动作时,供openDialog调用提交表单。
+            // 逐项验证必填字段(带 * 号)
+            var errors = [];
+
+            // 1. 证书名称
+            if (!$("#name").val()) {
+                errors.push("证书名称");
+            }
+            // 2. 证书编号
+            if (!$.trim($("#num").val())) {
+                errors.push("证书编号");
+            }
+            // 3. 发证日期
+            if (!$.trim($("#issuingDate").val())) {
+                errors.push("发证日期");
+            }
+            // 4. 专业
+            if (!$("#major").val()) {
+                errors.push("专业");
+            }
+            // 5. 取得方式
+            if (!$("#issType").val()) {
+                errors.push("取得方式");
+            }
+            // 6. 证书文件(新选了文件 或 已有旧文件,二选一即可)
+            var hasNewFile = $("#certificateFile")[0].files && $("#certificateFile")[0].files.length > 0;
+            var hasOldFile = '${not empty workStaffCertificate.temporaryUrl}' === 'true';
+            if (!hasNewFile && !hasOldFile) {
+                errors.push("证书文件");
+            }
+
+            if (errors.length > 0) {
+                parent.layer.msg("请填写或上传以下必填项:<br>" + errors.join("、"), {icon: 5, html: true, time: 3000});
+                return false;
+            }
+
+            // 7. 检查证书名称是否已存在(AJAX 异步验证)
+            /*var certName = $("#name").val();
+            var certId = $("#id").val() || '';
+            $.ajax({
+                url: '${ctx}/workstaff/workStaffBasicCertification/checkCertificateName',
+                type: 'post',
+                data: {name: certName, id: certId},
+                dataType: 'json',
+                success: function(data) {
+                    if (data.success) {
+                        $("#inputForm").submit();
+                    } else {
+                        parent.layer.msg(data.msg || '该证书名称已存在,不可重复添加!', {icon: 5, time: 3000});
+                    }
+                },
+                error: function() {
+                    parent.layer.msg('系统异常,证书名称重复检查失败!', {icon: 5, time: 3000});
+                }
+            });*/
+            $("#inputForm").submit();
+            return false;
+        }
+
+        $(document).ready(function() {
+            var billingContent = $("#billingContent").val();
+            if(8 != billingContent){
+                $(".contractType").hide();
+                $("#contractTypeDoc").attr("class","form-control");
+            }else{
+                $(".contractType").show();
+                $("#contractTypeDoc").attr("class","form-control required");
+            }
+
+
+
+
+            layui.use(['form', 'layer'], function () {
+                var form = layui.form;
+
+                $("#attachment_btn").click(function () {
+                    $("#attachment_file").click();
+                });
+
+            });
+
+            // 页面加载时初始化专业下拉框,传入当前专业值用于回显
+            loadMajorOptions(document.getElementById('name'), '${workStaffCertificate.major}');
+
+            validateForm = $("#inputForm").validate({
+                submitHandler: function(form){
+                    loading('正在提交,请稍等...');
+                    form.submit();
+                },
+                errorContainer: "#messageBox",
+                errorPlacement: function(error, element) {
+
+                    $("#messageBox").text("输入有误,请先更正。");
+                    if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){
+                        error.appendTo(element.parent().parent());
+                    } else {
+                        error.insertAfter(element);
+                    }
+                }
+            });
+
+            laydate.render({
+                elem: '#issuingDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+                , trigger: 'click'
+                , trigger: 'click'
+            });
+            laydate.render({
+                elem: '#registDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+                , trigger: 'click'
+                , trigger: 'click'
+            });
+
+        });
+
+
+
+        function addRow(list, idx, tpl, row){
+            var idx1 = $("#workAccountList tr").length;
+            if(list == '#workAccountList' && idx1 < 100){
+                bornTemplete(list, idx, tpl, row, idx1);
+            }
+        }
+        function bornTemplete(list, idx, tpl, row, idx1){
+            $(list).append(Mustache.render(tpl, {
+                idx: idx, delBtn: true, row: row,
+                order:idx1 + 1
+            }));
+            $(list+idx).find("select").each(function(){
+                $(this).val($(this).attr("data-value"));
+            });
+            $(list+idx).find("input[type='checkbox'], input[type='radio']").each(function(){
+                var ss = $(this).attr("data-value").split(',');
+                for (var i=0; i<ss.length; i++){
+                    if($(this).val() == ss[i]){
+                        $(this).attr("checked","checked");
+                    }
+                }
+            });
+        }
+
+        function delRow(obj, prefix) {
+            var id = $(prefix + "_invoiceId");
+            var delFlag = $(prefix + "_delFlag");
+            if (id.val() == "") {
+                $(obj).parent().parent().remove();
+            } else if (delFlag.val() == "0") {
+                delFlag.val("1");
+                $(obj).html("&divide;").attr("title", "撤回删除");
+                $(obj).parent().parent().addClass("error");
+                $(obj).parent().parent().addClass("hide");
+            } else if (delFlag.val() == "1") {
+                delFlag.val("0");
+                $(obj).html("&times;").attr("title", "删除");
+                $(obj).parent().parent().removeClass("error");
+            }
+            var length=$("#workInvoiceProjectRelationList tr").length;
+            var count=length;
+            for (var i=1;i<=length;i++) {
+                var delFlag = $("#workInvoiceProjectRelationList").find("tr").eq(i-1).find("input").eq(1).val();
+                if (delFlag == "1") {
+                    count =count-1;
+                }
+            }
+            if(count==1){
+                $("#chargeType").val("2")
+                layui.form.render();
+            }else if (count>1){
+                $("#chargeType").val("1")
+                layui.form.render();
+            }else if(count == 0){
+                $("#projectFlag").val("");
+            }
+        }
+
+        function insertTitleCollection(tValue){
+            var list = "${workInvoice.workAttachments}";
+            var size = (list.split('url')).length-1;
+            var files = tValue;
+            for(var i = 0;i<files.length;i++) {
+                var file = files[i];
+                var attachmentId = "";
+                var attachmentFlag = "115";
+                var timestamp = new Date().getTime();
+
+                var storeAs = "workInvoice";
+                var uploadPath = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com/" + storeAs;
+                /*将这段字符串存到数据库即可*/
+                var divId = "_attachment";
+                $("#addFile" + divId).show();
+                multipartUploadWithStsCollection(storeAs, file, attachmentId, attachmentFlag, uploadPath, divId, size);
+            }
+        }
+
+        // 根据证书名称级联加载专业选项
+        // selectedValue: 页面初始化时用于回显当前已选中的专业值
+        function loadMajorOptions(obj, selectedValue){
+            var certificateName = obj.value;
+            var $major = $("#major");
+            $major.html('<option value=""></option>');
+            if (certificateName == "") {
+                return;
+            }
+            $.ajax({
+                type: "post",
+                url: '${ctx}/workstaff/workStaffBasicInfo/getMessage',
+                data: {"type": certificateName},
+                dataType: "json",
+                async: false,
+                success: function(data){
+                    if(data.success) {
+                        for (var info in data.data){
+                            if (selectedValue && data.data[info].value == selectedValue) {
+                                $major.append(new Option(data.data[info].label, data.data[info].value, true, true));
+                            } else {
+                                $major.append(new Option(data.data[info].label, data.data[info].value));
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        // 触发隐藏的文件选择框,根据证书名称判断是否限制文件类型
+        function triggerFileUpload() {
+            var certName = $("#name").val();
+            var pdfOnlyNames = ['161', '151', '181'];
+            var $fileInput = $("#certificateFile");
+            if (pdfOnlyNames.indexOf(certName) >= 0) {
+                $fileInput.attr('accept', '.pdf');
+            } else {
+                $fileInput.removeAttr('accept');
+            }
+            $fileInput.click();
+        }
+
+        // 文件选择后预览(仅前端预览,未上传服务器)
+        function onFileSelected(obj) {
+            var file = obj.files[0];
+            if (!file) return;
+
+            // 一级造价师、一级建造师、一级监理师仅允许上传 PDF
+            var certName = $("#name").val();
+            var pdfOnlyNames = ['161', '151', '181'];
+            if (pdfOnlyNames.indexOf(certName) >= 0) {
+                var ext = file.name.split('.').pop().toLowerCase();
+                if (ext !== 'pdf') {
+                    layer.msg("当前证书名称仅支持上传 PDF 文件!", {icon: 2});
+                    obj.value = "";
+                    return;
+                }
+            }
+
+            if (file.size > 50 * 1024 * 1024) {
+                layer.msg("请上传50M以内文件!", {icon: 2});
+                obj.value = "";
+                return;
+            }
+
+            // 生成 blob 地址
+            var url = window.URL.createObjectURL(file);
+            var ext = file.name.split('.').pop().toLowerCase();
+
+            // 可预览的类型:图片 + PDF
+            var previewableTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'pdf'];
+            var isPreviewable = previewableTypes.indexOf(ext) >= 0;
+
+            var $preview = $("#filePreviewArea");
+            var html = '';
+
+            if (isPreviewable) {
+                // 图片/PDF:正常预览
+                if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].indexOf(ext) >= 0) {
+                    html = '<img src="' + url + '" width="80" height="80"'
+                        + ' style="cursor:pointer; vertical-align:middle;"'
+                        + ' onclick="previewLocalFile(\'' + url + '\')"'
+                        + ' alt="图片预览" title="点击预览图片"/>'
+                        + ' <span style="margin-left:5px;color:#666;font-size:12px;">' + file.name + '</span>';
+                } else {
+                    // PDF
+                    html = '<i class="fa fa-file-pdf-o" style="font-size:18px;margin-right:5px;color:#d9534f;"></i>'
+                        + '<a class="file-link" href="javascript:void(0)"'
+                        + ' onclick="previewLocalFile(\'' + url + '\')"'
+                        + ' title="点击预览PDF文件">' + file.name + '</a>';
+                }
+            } else {
+                // 非预览文件(docx/xlsx/zip等):去掉预览链接,改成提示
+                html = '<i class="fa fa-file" style="font-size:18px;margin-right:5px;color:#666;"></i>'
+                    + '<span style="color:#666;">' + file.name + '</span>'
+                    + '<span style="color:#999;font-size:12px;margin-left:5px;">(仅支持下载)</span>';
+            }
+
+            // 下载和移除按钮,所有文件都保留
+            html += ' <a href="javascript:void(0)" onclick="downloadFile(\'' + url + '\',\'' + file.name + '\')" title="下载文件" style="color:#28a745;margin-left:5px;text-decoration:none;"><i class="fa fa-download"></i></a>'
+                + ' <a class="remove-file-btn" onclick="removeNewFile()" title="移除文件"><i class="fa fa-times"></i></a>';
+
+            $preview.html(html);
+        }
+
+        // 移除新选择的文件,恢复原有文件展示
+        function removeNewFile() {
+            $("#certificateFile").val("");
+            var $preview = $("#filePreviewArea");
+            var $original = $("#originalFileDisplay");
+            if ($original.html().trim() !== "") {
+                $preview.html($original.html());
+            } else {
+                $preview.html('<span style="color:#999;font-size:12px;">未选择文件</span>');
+            }
+        }
+
+        // 本地文件预览(未上传服务器的blob URL)
+        function previewLocalFile(url) {
+            parent.layer.open({
+                type: 2,
+                area: ['90%', '90%'],
+                title: '文件预览',
+                maxmin: true,
+                shade: 0.2,
+                shadeClose: true,
+                content: url,
+                btn: ['关闭'],
+                cancel: function(index){}
+            });
+        }
+
+        // 下载文件(通过 fetch 获取 blob,解决跨域时浏览器直接打开而非下载的问题)
+        function downloadFile(fileUrl, fileName) {
+            fetch(fileUrl)
+                .then(function(resp) { return resp.blob(); })
+                .then(function(blob) {
+                    var blobUrl = window.URL.createObjectURL(blob);
+                    var a = document.createElement('a');
+                    a.href = blobUrl;
+                    a.download = fileName || 'download';
+                    document.body.appendChild(a);
+                    a.click();
+                    document.body.removeChild(a);
+                    window.URL.revokeObjectURL(blobUrl);
+                })
+                .catch(function() {
+                    window.open(fileUrl, '_blank');
+                });
+        }
+
+
+    </script>
+</head>
+<body>
+<div class="single-form">
+    <div class="container">
+        <form:form id="inputForm" modelAttribute="workStaffCertificate" action="${ctx}/workstaff/workStaffBasicCertification/save" enctype="multipart/form-data" method="post" class="layui-form">
+            <form:hidden path="id"/>
+
+            <div class="form-group layui-row first">
+                <div class="form-group-label"><h2>证书详情</h2></div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书名称:</label>
+                    <div class="layui-input-block readOnlyFFF">
+                        <form:select  path="name" id="name" class="form-control simple-select required" onchange="loadMajorOptions(this)">
+                            <form:options items="${fns:getMainDictList('certificate_type')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
+                        </form:select>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书编号:</label>
+                    <div class="layui-input-block">
+                        <form:input id="num"  path="num" htmlEscape="false" class="form-control required layui-input"/>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label">发证机关:</label>
+                    <div class="layui-input-block">
+                        <form:input id="issuingAuthority"  path="issuingAuthority" htmlEscape="false" class="form-control layui-input"/>
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>发证日期:</label>
+                    <div class="layui-input-block">
+                        <input placeholder="请选择发证日期" style="background-color: #fff" class="laydate-icondate form-control layui-input layer-date required laydate-icon" readonly="readonly" id="issuingDate" name="issuingDate" value="<fmt:formatDate value="${workStaffCertificate.issuingDate}" pattern="yyyy-MM-dd"/>">
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label">注册日期:</label>
+                    <div class="layui-input-block">
+                        <input placeholder="请选择注册日期" style="background-color: #fff" class="laydate-icondate form-control layui-input layer-date laydate-icon" readonly="readonly" id="registDate" name="registDate" value="<fmt:formatDate value="${workStaffCertificate.registDate}" pattern="yyyy-MM-dd"/>">
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label double-line">注册证书编号:</label>
+                    <div class="layui-input-block">
+                        <form:input id="registNum"  path="registNum" htmlEscape="false" class="form-control layui-input"/>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6 lw6">
+                    <label class="layui-form-label"><span class="require-item">*</span>专业</label>
+                    <div class="layui-input-block readOnlyFFF">
+                        <select id="major" name="major" value="${workStaffCertificate.major}" lay-verify="required" lay-filter="majorFilter"></select>
+                    </div>
+                </div>
+
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>取得方式:</label>
+                    <div class="layui-input-block readOnlyFFF">
+                        <select name="issType" id="issType" class="form-control simple-select required">
+                            <option value="考试" <c:if test="${'考试' eq workStaffCertificate.issType}">selected</c:if>>考试</option>
+                            <option value="考核" <c:if test="${'考核' eq workStaffCertificate.issType}">selected</c:if>>考核</option>
+                        </select>
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm12 file-upload-section">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书文件:</label>
+                    <div class="layui-input-block">
+                        <a class="upload-btn" onclick="triggerFileUpload()">
+                            <i class="fa fa-upload"></i>&nbsp;上传/替换文件
+                        </a>
+                        <input id="certificateFile" name="file" type="file" style="display:none" onchange="onFileSelected(this)"/>
+                        <div class="file-preview-area" id="filePreviewArea" style="line-height: 40px">
+                            <c:choose>
+                                <c:when test="${not empty workStaffCertificate.temporaryUrl}">
+                                    <c:set var="certFileName" value="${workStaffCertificate.fileName}" />
+                                    <c:set var="certExt" value="${fn:toLowerCase(fn:substringAfter(certFileName, '.'))}" />
+                                    <c:choose>
+                                        <c:when test="${certExt == 'jpg' || certExt == 'jpeg' || certExt == 'png' || certExt == 'gif' || certExt == 'bmp' || certExt == 'webp'}">
+                                            <img src="${workStaffCertificate.temporaryUrl}" width="80" height="80" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')" alt="${fns:abbr(workStaffCertificate.fileName,30)}"/>
+
+                                        </c:when>
+                                        <c:otherwise>
+                                            <c:choose>
+                                                <c:when test="${fn:containsIgnoreCase(workStaffCertificate.fileName,'pdf')}">
+                                                    <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')">${workStaffCertificate.fileName}</a>
+                                                </c:when>
+                                                <c:otherwise>
+                                                    <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%')">${workStaffCertificate.fileName}</a>
+                                                </c:otherwise>
+                                            </c:choose>
+                                        </c:otherwise>
+                                    </c:choose>
+                                    <a href="javascript:void(0);" onclick="downloadFile('${workStaffCertificate.temporaryUrl}','${workStaffCertificate.fileName}')"
+                                       title="下载 ${workStaffCertificate.fileName}"
+                                       style="color:#28a745;margin-left:5px;text-decoration:none;">
+                                        <i class="fa fa-download"></i>
+                                    </a>
+                                </c:when>
+                                <c:otherwise>
+                                    <span style="color:#999;font-size:12px;">未上传文件</span>
+                                </c:otherwise>
+                            </c:choose>
+                        </div>
+                        <span id="originalFileDisplay" style="display:none;">
+                            <c:if test="${not empty workStaffCertificate.temporaryUrl}">
+                                <c:set var="certFileName2" value="${workStaffCertificate.fileName}" />
+                                <c:set var="certExt2" value="${fn:toLowerCase(fn:substringAfter(certFileName2, '.'))}" />
+                                <c:choose>
+                                    <c:when test="${certExt2 == 'jpg' || certExt2 == 'jpeg' || certExt2 == 'png' || certExt2 == 'gif' || certExt2 == 'bmp' || certExt2 == 'webp'}">
+                                        <img src="${workStaffCertificate.temporaryUrl}" width="80" height="80" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')" alt="${fns:abbr(workStaffCertificate.fileName,30)}"/>
+                                    </c:when>
+                                    <c:otherwise>
+                                        <c:choose>
+                                            <c:when test="${fn:containsIgnoreCase(workStaffCertificate.fileName,'pdf')}">
+                                                <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')">${workStaffCertificate.fileName}</a>
+                                            </c:when>
+                                            <c:otherwise>
+                                                <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%')">${workStaffCertificate.fileName}</a>
+                                            </c:otherwise>
+                                        </c:choose>
+                                    </c:otherwise>
+                                </c:choose>
+                                <a href="javascript:void(0);" onclick="downloadFile('${workStaffCertificate.temporaryUrl}','${workStaffCertificate.fileName}')"
+                                   title="下载 ${workStaffCertificate.fileName}"
+                                   style="color:#28a745;margin-left:5px;text-decoration:none;">
+                                    <i class="fa fa-download"></i>
+                                </a>
+                            </c:if>
+                        </span>
+                    </div>
+
+                    <span style="color:#ff5722;font-size:12px;">
+                            若为:一级造价师、一级建造师、一级监理师请。上传证书网站上下载的pdf原文件
+                        </span>
+                </div>
+            </div>
+
+
+            <div class="form-group layui-row page-end"></div>
+        </form:form>
+    </div>
+</div>
+
+</body>
+</html>

+ 291 - 0
src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationHistoryList.jsp

@@ -0,0 +1,291 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/webpage/include/taglib.jsp"%>
+<html>
+<head>
+	<title>员工资格证书历史管理</title>
+	<meta name="decorator" content="default"/>
+    <style>
+        body{
+            background-color:transparent;
+            filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#26FFFFFF, endColorstr=#26FFFFFF);
+            color:#ffffff;
+            background-color:rgba(255,255,255,0);
+            height:100%;
+        }
+    </style>
+    <script type="text/javascript">
+        $(document).ready(function() {
+            $("#cus_name").show();
+            $("#cus_name").siblings().hide();
+            //搜索框收放
+            $('#moresee').click(function(){
+                if($('#moresees').is(':visible'))
+                {
+                    $('#moresees').slideUp(0,resizeListWindow1);
+                    $('#moresee i').removeClass("glyphicon glyphicon-menu-up").addClass("glyphicon glyphicon-menu-down");
+                }else{
+                    $('#moresees').slideDown(0,resizeListWindow1);
+                    $('#moresee i').removeClass("glyphicon glyphicon-menu-down").addClass("glyphicon glyphicon-menu-up");
+                }
+            });
+            laydate.render({
+                elem: '#validStartDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+, trigger: 'click'
+            });
+            laydate.render({
+                elem: '#validEndDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+, trigger: 'click'
+            });
+
+        });
+
+        function openDialog(title,url,width,height,target) {
+            if (navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i)) {//如果是移动端,就使用自适应大小弹窗
+                width = 'auto';
+                height = 'auto';
+            } else {//如果是PC端,根据用户设置的width和height显示。
+
+            }
+            top.layer.open({
+                type: 2,
+                area: [width, height],
+                title: title,
+                maxmin: true, //开启最大化最小化按钮
+                content: url,
+                skin: 'three-btns',
+                btn: ["提交","关闭"],
+                btn1: function(index, layero){
+                    var body = top.layer.getChildFrame('body', index);
+                    var iframeWin = layero.find('iframe')[0]; //得到iframe页的窗口对象,执行iframe页的方法:iframeWin.method();
+                    var inputForm = body.find('#inputForm');
+                    var top_iframe;
+                    if(target){
+                        top_iframe = target;//如果指定了iframe,则在改frame中跳转
+                    }else{
+                        top_iframe = top.getActiveTab().attr("name");//获取当前active的tab的iframe
+                    }
+                    inputForm.attr("target",top_iframe);//表单提交成功后,从服务器返回的url在当前tab中展示
+
+                    if(iframeWin.contentWindow.doSubmit(index) ){
+                        // top.layer.close(index);//关闭对话框。
+                        setTimeout(function(){top.layer.close(index)}, 100);//延时0.1秒,对应360 7.1版本bug
+                    }
+                    return false;
+
+
+                },
+                btn2: function (index) {
+
+                }
+            });
+        }
+
+	</script>
+</head>
+<body>
+<div class="wrapper wrapper-content">
+    <sys:message content="${message}"/>
+    <div class="list-form-tab contentShadow shadowLTR" id="tabDiv">
+        <ul class="list-tabs" >
+            <li><a href="${ctx}/workstaff/workStaffBasicCertification/list">资格证书</a></li>
+            <li class="active"><a href="${ctx}/workstaff/workStaffBasicCertificationHistory/list">变更历史</a></li>
+        </ul>
+    </div>
+    <div class="layui-row">
+        <div class="full-width fl">
+            <div class="contentShadow layui-row" id="queryDiv">
+            <form:form id="searchForm" modelAttribute="workStaffCertificate" action="${ctx}/workstaff/workStaffBasicCertificationHistory/" method="post" class="form-inline">
+                <input id="pageNo" name="pageNo" type="hidden" value="${page.pageNo}"/>
+                <input id="pageSize" name="pageSize" type="hidden" value="${page.pageSize}"/>
+                <input type="hidden" input="params" value="">
+                <input id="toflag" name="toflag" type="hidden" value="1"/>
+                <table:sortColumn id="orderBy" name="orderBy" value="${page.orderBy}" callback="sortOrRefresh();"/><!-- 支持排序 -->
+                <div class="commonQuery lw6">
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">资格证书:</label>
+                        <div class="layui-input-block">
+                            <form:select path="name"  class="form-control simple-select">
+                                <form:option value="" label=""/>
+                                <form:options items="${fns:getMainDictList('certificate_type')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
+                            </form:select>
+                        </div>
+                    </div>
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">使用有效期:</label>
+                        <div class="layui-input-block readOnlyFFF">
+                            <input id="validStartDate" name="validStartDate" type="text" placeholder="开始时间" readonly="readonly" maxlength="20" class="laydate-icondate form-control layer-date layui-input laydate-icon query-group"
+                                   value="<fmt:formatDate value="${workStaffCertificate.validStartDate}" pattern="yyyy-MM-dd"/>"/>
+                            </input>
+                            <span class="group-sep">-</span>
+                            <input id="validEndDate" name="validEndDate" type="text" placeholder="结束时间" readonly="readonly" maxlength="20" class="laydate-icondate form-control layer-date layui-input laydate-icon query-group"
+                                   value="<fmt:formatDate value="${workStaffCertificate.validEndDate}" pattern="yyyy-MM-dd"/>"/>
+                            </input>
+                        </div>
+                    </div>
+                    <div class="layui-item athird fr">
+                        <div class="input-group">
+                            <a href="#" id="moresee"><i class="glyphicon glyphicon-menu-down"></i></a>
+                            <div class="layui-btn-group search-spacing">
+                                <button id="searchQuery" class="layui-btn layui-btn-sm layui-bg-blue" onclick="search()">查询</button>
+                                <button id="searchReset" class="layui-btn layui-btn-sm " onclick="resetSearch()">重置</button>
+                            </div>
+                        </div>
+                    </div>
+                    <div style="    clear:both;"></div>
+                </div>
+                <div id="moresees" class="lw6" style="clear:both;display:none;">
+
+
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">证书所属人:</label>
+                        <div class="layui-input-block with-icon">
+                            <sys:inquireselectUserNotReadolnyTow id="possessUser" name="possessUserId" value="${workStaffCertificate.possessUserId}" labelName="possessUserName" labelValue="${workStaffCertificate.possessUserName}" cssStyle="background-color: #fff"
+                                                                 title="用户" url="/sys/office/treeDataAll?type=3" cssClass="form-control layui-input" allowClear="true" notAllowSelectParent="true"/>
+                        </div>
+                    </div>
+
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">所属人部门:</label>
+                        <div class="layui-input-block with-icon">
+                            <sys:treeselect id="officeId" name="officeId" value="${workStaffCertificate.officeId}" labelName="officeName" labelValue="${workStaffCertificate.officeName}" cssStyle="background-color: #fff"
+                                            title="部门" url="/sys/office/treeDataAll?type=6" cssClass="form-control layui-input" allowInput="true" allowClear="true" notAllowSelectParent="true"/>
+                        </div>
+                    </div>
+                </div>
+            </form:form>
+	        </div>
+	    </div>
+
+        <div class="full-width fl">
+            <div class="contentShadow layui-form contentDetails">
+                <div class="nav-btns">
+                    <div class="layui-btn-group">
+                        <button class="layui-btn layui-btn-sm" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
+                    </div>
+                    <div style="clear: both;"></div>
+                </div>
+                <table class="oa-table layui-table" id="contentTable" lay-filter="tableEvent"></table>
+
+                <!-- 分页代码 -->
+                <table:page page="${page}"></table:page>
+                <div style="clear: both;"></div>
+            </div>
+        </div>
+    </div>
+    <div id="changewidth"></div>
+</div>
+<script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+<script charset="utf-8" src="${ctxStatic}/kindeditor/plugins/tableEdit/tableEdit.js"></script>
+<script>
+    layui.use(['table','tableEdit','layer'], function () {
+        var table = layui.table,tableEdit = layui.tableEdit,$ = layui.$;
+
+        var cols = layui.table.render({
+            limit:${ page.pageSize }
+            ,elem: '#contentTable'
+            ,page: false
+            ,cols: [[
+                /*{checkbox: true, fixed: true},*/
+                {field:'index',align:'center', title: '序号',width:40}
+                ,{field:'name',align:'center', title: '证书名称',minWidth:100,templet:function(d){
+                        var xml = "<a class=\"attention-info\" href=\"javascript:void(0);\" onclick=\"openDialogView('查看员工信息', '${ctx}/workstaff/workStaffBasicCertificationHistory/view?id="+d.id+"','95%','95%')\">" +
+                            "<span title=" + d.name + ">" + d.name + "</span></a>";
+                        return xml;
+                    }}
+                ,{field:'num',align:'center', title: '证书编号',minWidth:150}
+                ,{field:'issuingAuthority',align:'center', title: '发证机关', minWidth:100}
+                ,{field:'issuingDate',align:'center', title: '发证日期', minWidth:100}
+                ,{field:'registDate',align:'center', title: '注册日期', minWidth:100}
+                ,{field:'registNum',align:'center', title: '注册证书编号', minWidth:100}
+
+                ,{field:'major',align:'center', title: '专业', minWidth:80}
+                ,{field:'issType',align:'center', title: '取得方式', minWidth:80}
+                ,{field:'validStartDate', align:'center',title: '使用有效期开始时间',width:160}
+                ,{field:'validEndDate', align:'center',title: '使用有效期结束时间',width:160}
+                ,{field:'userName', align:'center',title: '证书所属人',width:100}
+                ,{field:'officeName', align:'center',title: '所属人部门',width:100}
+                ,{field: 'filePath', align: 'center', title: '文件信息', minWidth: 200, templet: function(d) {
+                        if(2 == d.uploadMode){
+                            if(d.fileName.toLowerCase().indexOf('jpg') != -1 || d.fileName.toLowerCase().indexOf('png') != -1 || d.fileName.toLowerCase().indexOf('gif') != -1 || d.fileName.toLowerCase().indexOf('bmp') != -1 || d.fileName.toLowerCase().indexOf('jpeg') != -1){
+                                return "<img src="+d.temporaryUrl+" width='50' height='50' onclick=\"openDialogView('预览','${ctx}/sys/picturepreview/picturePreview?url=" + d.temporaryUrl +"','90%','90%')\" alt='"+d.fileName+"'>";
+                            }else if(d.fileName.toLowerCase().indexOf('pdf') != -1){
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.temporaryUrl +"',1)\">" + d.fileName + "</a>";
+                            }else{
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.temporaryUrl +"',2)\">" + d.fileName + "</a>";
+                            }
+                        }else{
+                            if(d.fileName.toLowerCase().indexOf('jpg') != -1 || d.fileName.toLowerCase().indexOf('png') != -1 || d.fileName.toLowerCase().indexOf('gif') != -1 || d.fileName.toLowerCase().indexOf('bmp') != -1 || d.fileName.toLowerCase().indexOf('jpeg') != -1){
+                                return "<img src="+d.filePath+" width='50' height='50' onclick=\"openDialogView('预览','${ctx}/sys/picturepreview/picturePreview?url=" + d.filePath +"','90%','90%')\" alt='"+d.fileName+"'>";
+                            }else if(d.fileName.toLowerCase().indexOf('pdf') != -1){
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.filePath +"',1)\">" + d.fileName + "</a>";
+                            }else{
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.filePath +"',2)\">" + d.fileName + "</a>";
+                            }
+                        }
+                    }}
+            ]]
+            ,data: [
+                <c:if test="${ not empty page.list}">
+                <c:forEach items="${page.list}" var="workStaffCertificate" varStatus="index">
+                <c:if test="${index.index != 0}">,</c:if>
+                {
+                    "index": "${index.index+1}"
+                    ,
+                    "id": "${workStaffCertificate.id}"
+                    ,
+                    "name": "${workStaffCertificate.name}"
+                    ,
+                    "num": "<c:out value="${workStaffCertificate.num}" escapeXml="true"/>"
+                    ,
+                    'issuingAuthority': "<c:out value="${workStaffCertificate.issuingAuthority}" escapeXml="true"/>"
+                    ,
+                    "issuingDate": "<fmt:formatDate value="${workStaffCertificate.issuingDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "registDate": "<fmt:formatDate value="${workStaffCertificate.registDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "registNum": "${workStaffCertificate.registNum}"
+                    ,
+                    "major": "${workStaffCertificate.major}"
+                    ,
+                    "issType": "${workStaffCertificate.issType}"
+                    ,
+                    "validStartDate": "<fmt:formatDate value="${workStaffCertificate.validStartDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "validEndDate": "<fmt:formatDate value="${workStaffCertificate.validEndDate}" pattern="yyyy-MM-dd"/>"
+
+                    ,"uploadMode": "<c:out value='${workStaffCertificate.uploadMode}'/>"
+                    ,"fileName": "<c:out value='${workStaffCertificate.fileName}'/>"
+                    ,"temporaryUrl": "<c:out value='${workStaffCertificate.temporaryUrl}'/>"
+                    ,"filePath": "<c:out value='${workStaffCertificate.filePath}'/>"
+                    ,"userName": "<c:out value='${workStaffCertificate.userName}'/>"
+                    ,"officeName": "<c:out value='${workStaffCertificate.officeName}'/>"
+
+                    <shiro:hasPermission name="workstaff:workStaffBasicCertification:edit">,
+                    "canedit": "1"</shiro:hasPermission>
+                    <shiro:hasPermission name="workstaff:workStaffBasicCertification:del">,
+                    "candelete": "1"</shiro:hasPermission>
+                }
+                </c:forEach>
+                </c:if>
+            ]
+        }).config.cols;
+
+    })
+
+    resizeListTable(130);/*消除由于有竖向滚动条造成table出现横向滚动条*/
+    $("a").on("click",addLinkVisied);
+</script>
+<script>
+    resizeListWindow1();
+    $(window).resize(function(){
+        resizeListWindow1();
+    });
+
+
+</script>
+</body>
+</html>

+ 453 - 0
src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationList.jsp

@@ -0,0 +1,453 @@
+<%@ page import="com.jeeplus.modules.sys.utils.UserUtils" %>
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/webpage/include/taglib.jsp"%>
+<%
+    boolean admin = UserUtils.getUser().isAdmin();
+%>
+<html>
+<head>
+	<title>员工资格证书管理</title>
+	<meta name="decorator" content="default"/>
+    <style>
+        body{
+            background-color:transparent;
+            filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#26FFFFFF, endColorstr=#26FFFFFF);
+            color:#ffffff;
+            background-color:rgba(255,255,255,0);
+            height:100%;
+        }
+    </style>
+    <script type="text/javascript">
+        $(document).ready(function() {
+            $("#cus_name").show();
+            $("#cus_name").siblings().hide();
+            //搜索框收放
+            $('#moresee').click(function(){
+                if($('#moresees').is(':visible'))
+                {
+                    $('#moresees').slideUp(0,resizeListWindow1);
+                    $('#moresee i').removeClass("glyphicon glyphicon-menu-up").addClass("glyphicon glyphicon-menu-down");
+                }else{
+                    $('#moresees').slideDown(0,resizeListWindow1);
+                    $('#moresee i').removeClass("glyphicon glyphicon-menu-down").addClass("glyphicon glyphicon-menu-up");
+                }
+            });
+            laydate.render({
+                elem: '#validStartDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+, trigger: 'click'
+            });
+            laydate.render({
+                elem: '#validEndDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+, trigger: 'click'
+            });
+
+        });
+
+        function openDialog(title,url,width,height,target) {
+            if (navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i)) {//如果是移动端,就使用自适应大小弹窗
+                width = 'auto';
+                height = 'auto';
+            } else {//如果是PC端,根据用户设置的width和height显示。
+
+            }
+            top.layer.open({
+                type: 2,
+                area: [width, height],
+                title: title,
+                maxmin: true, //开启最大化最小化按钮
+                content: url,
+                skin: 'three-btns',
+                btn: ["提交","关闭"],
+                btn1: function(index, layero){
+                    var body = top.layer.getChildFrame('body', index);
+                    var iframeWin = layero.find('iframe')[0]; //得到iframe页的窗口对象,执行iframe页的方法:iframeWin.method();
+                    var inputForm = body.find('#inputForm');
+                    var top_iframe;
+                    if(target){
+                        top_iframe = target;//如果指定了iframe,则在改frame中跳转
+                    }else{
+                        top_iframe = top.getActiveTab().attr("name");//获取当前active的tab的iframe
+                    }
+                    inputForm.attr("target",top_iframe);//表单提交成功后,从服务器返回的url在当前tab中展示
+
+                    if(iframeWin.contentWindow.doSubmit(index) ){
+                        // top.layer.close(index);//关闭对话框。
+                        setTimeout(function(){top.layer.close(index)}, 100);//延时0.1秒,对应360 7.1版本bug
+                    }
+                    return false;
+
+
+                },
+                btn2: function (index) {
+
+                }
+            });
+        }
+
+	</script>
+</head>
+<body>
+<div class="wrapper wrapper-content">
+    <sys:message content="${message}"/>
+    <div class="list-form-tab contentShadow shadowLTR" id="tabDiv">
+        <ul class="list-tabs" >
+            <li class="active"><a href="${ctx}/workstaff/workStaffBasicCertification/list">资格证书</a></li>
+            <li><a href="${ctx}/workstaff/workStaffBasicCertificationHistory/list">变更历史</a></li>
+        </ul>
+    </div>
+    <div class="layui-row">
+        <div class="full-width fl">
+            <div class="contentShadow layui-row" id="queryDiv">
+            <form:form id="searchForm" modelAttribute="workStaffCertificate" action="${ctx}/workstaff/workStaffBasicCertification/" method="post" class="form-inline">
+                <input id="pageNo" name="pageNo" type="hidden" value="${page.pageNo}"/>
+                <input id="pageSize" name="pageSize" type="hidden" value="${page.pageSize}"/>
+                <input type="hidden" input="params" value="">
+                <input id="toflag" name="toflag" type="hidden" value="1"/>
+                <table:sortColumn id="orderBy" name="orderBy" value="${page.orderBy}" callback="sortOrRefresh();"/><!-- 支持排序 -->
+                <div class="commonQuery lw6">
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">资格证书:</label>
+                        <div class="layui-input-block">
+                            <form:select path="name"  class="form-control simple-select">
+                                <form:option value="" label=""/>
+                                <form:options items="${fns:getMainDictList('certificate_type')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
+                            </form:select>
+                        </div>
+                    </div>
+                    <div class="layui-item query athird">
+                        <label class="layui-form-label">使用有效期:</label>
+                        <div class="layui-input-block readOnlyFFF">
+                            <input id="validStartDate" name="validStartDate" type="text" placeholder="开始时间" readonly="readonly" maxlength="20" class="laydate-icondate form-control layer-date layui-input laydate-icon query-group"
+                                   value="<fmt:formatDate value="${workStaffCertificate.validStartDate}" pattern="yyyy-MM-dd"/>"/>
+                            </input>
+                            <span class="group-sep">-</span>
+                            <input id="validEndDate" name="validEndDate" type="text" placeholder="结束时间" readonly="readonly" maxlength="20" class="laydate-icondate form-control layer-date layui-input laydate-icon query-group"
+                                   value="<fmt:formatDate value="${workStaffCertificate.validEndDate}" pattern="yyyy-MM-dd"/>"/>
+                            </input>
+                        </div>
+                    </div>
+                    <div class="layui-item athird fr">
+                        <div class="input-group">
+                            <a href="#" id="moresee"><i class="glyphicon glyphicon-menu-down"></i></a>
+                            <div class="layui-btn-group search-spacing">
+                                <button id="searchQuery" class="layui-btn layui-btn-sm layui-bg-blue" onclick="search()">查询</button>
+                                <button id="searchReset" class="layui-btn layui-btn-sm " onclick="resetSearch()">重置</button>
+                            </div>
+                        </div>
+                    </div>
+                    <div style="    clear:both;"></div>
+                </div>
+                <div id="moresees" class="lw6" style="clear:both;display:none;">
+
+
+                        <div class="layui-item query athird">
+                            <label class="layui-form-label">证书所属人:</label>
+                            <div class="layui-input-block with-icon">
+                                <sys:inquireselectUserNotReadolnyTow id="possessUser" name="possessUserId" value="${workStaffCertificate.possessUserId}" labelName="possessUserName" labelValue="${workStaffCertificate.possessUserName}" cssStyle="background-color: #fff"
+                                                                     title="用户" url="/sys/office/treeDataAll?type=3" cssClass="form-control layui-input" allowClear="true" notAllowSelectParent="true"/>
+                            </div>
+                        </div>
+
+                        <div class="layui-item query athird">
+                            <label class="layui-form-label">所属人部门:</label>
+                            <div class="layui-input-block with-icon">
+                                <sys:treeselect id="officeId" name="officeId" value="${workStaffCertificate.officeId}" labelName="officeName" labelValue="${workStaffCertificate.officeName}" cssStyle="background-color: #fff"
+                                                title="部门" url="/sys/office/treeDataAll?type=6" cssClass="form-control layui-input" allowInput="true" allowClear="true" notAllowSelectParent="true"/>
+                            </div>
+                        </div>
+                </div>
+            </form:form>
+	        </div>
+	    </div>
+
+        <div class="full-width fl">
+            <div class="contentShadow layui-form contentDetails">
+                <div class="nav-btns">
+                    <div class="layui-btn-group">
+                        <shiro:hasPermission name="workstaff:workStaffBasicCertification:add">
+                            <button type="button" class="layui-btn layui-btn-sm" onclick="checkHandSignatureAndOpen('${ctx}/workstaff/workStaffBasicCertification/form', '个人资质信息')"> 增加</button>
+                        </shiro:hasPermission>
+                        <shiro:hasPermission name="workstaff:workStaffBasicCertification:alterationSignature">
+                            <button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-blue" id="exportAll"> 变更手签章</button>
+                        </shiro:hasPermission>
+                        <button class="layui-btn layui-btn-sm" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
+                    </div>
+                    <div style="clear: both;"></div>
+                </div>
+                <table class="oa-table layui-table" id="contentTable" lay-filter="tableEvent"></table>
+
+                <!-- 分页代码 -->
+                <table:page page="${page}"></table:page>
+                <div style="clear: both;"></div>
+            </div>
+        </div>
+    </div>
+    <div id="changewidth"></div>
+</div>
+<script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+<script charset="utf-8" src="${ctxStatic}/kindeditor/plugins/tableEdit/tableEdit.js"></script>
+<script>
+    layui.use(['table','tableEdit','layer'], function () {
+        var table = layui.table,tableEdit = layui.tableEdit,$ = layui.$;
+
+        var cols = layui.table.render({
+            limit:${ page.pageSize }
+            ,elem: '#contentTable'
+            ,page: false
+            ,cols: [[
+                /*{checkbox: true, fixed: true},*/
+                {field:'index',align:'center', title: '序号',width:40}
+                ,{field:'name',align:'center', title: '证书名称',minWidth:100,templet:function(d){
+                        var xml = "<a class=\"attention-info\" href=\"javascript:void(0);\" onclick=\"openDialogView('查看员工信息', '${ctx}/workstaff/workStaffBasicCertification/view?id="+d.id+"','95%','95%')\">" +
+                            "<span title=" + d.name + ">" + d.name + "</span></a>";
+                        return xml;
+                    }}
+                ,{field:'num',align:'center', title: '证书编号',minWidth:150}
+                ,{field:'issuingAuthority',align:'center', title: '发证机关', minWidth:100}
+                ,{field:'issuingDate',align:'center', title: '发证日期', minWidth:100}
+                ,{field:'registDate',align:'center', title: '注册日期', minWidth:100}
+                ,{field:'registNum',align:'center', title: '注册证书编号', minWidth:100}
+
+                ,{field:'major',align:'center', title: '专业', minWidth:80}
+                ,{field:'issType',align:'center', title: '取得方式', minWidth:80}
+                ,{field:'validStartDate', align:'center',title: '使用有效期开始时间',width:160}
+                ,{field:'validEndDate', align:'center',title: '使用有效期结束时间',width:160}
+                ,{field:'userName', align:'center',title: '证书所属人',width:100}
+                ,{field:'officeName', align:'center',title: '所属人部门',width:100}
+                ,{field: 'filePath', align: 'center', title: '文件信息', minWidth: 200, templet: function(d) {
+                        if(2 == d.uploadMode){
+                            if(d.fileName.toLowerCase().indexOf('jpg') != -1 || d.fileName.toLowerCase().indexOf('png') != -1 || d.fileName.toLowerCase().indexOf('gif') != -1 || d.fileName.toLowerCase().indexOf('bmp') != -1 || d.fileName.toLowerCase().indexOf('jpeg') != -1){
+                                return "<img src="+d.temporaryUrl+" width='50' height='50' onclick=\"openDialogView('预览','${ctx}/sys/picturepreview/picturePreview?url=" + d.temporaryUrl +"','90%','90%')\" alt='"+d.fileName+"'>";
+                            }else if(d.fileName.toLowerCase().indexOf('pdf') != -1){
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.temporaryUrl +"',1)\">" + d.fileName + "</a>";
+                            }else{
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.temporaryUrl +"',2)\">" + d.fileName + "</a>";
+                            }
+                        }else{
+                            if(d.fileName.toLowerCase().indexOf('jpg') != -1 || d.fileName.toLowerCase().indexOf('png') != -1 || d.fileName.toLowerCase().indexOf('gif') != -1 || d.fileName.toLowerCase().indexOf('bmp') != -1 || d.fileName.toLowerCase().indexOf('jpeg') != -1){
+                                return "<img src="+d.filePath+" width='50' height='50' onclick=\"openDialogView('预览','${ctx}/sys/picturepreview/picturePreview?url=" + d.filePath +"','90%','90%')\" alt='"+d.fileName+"'>";
+                            }else if(d.fileName.toLowerCase().indexOf('pdf') != -1){
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.filePath +"',1)\">" + d.fileName + "</a>";
+                            }else{
+                                return "<a class=\"attention-info\" title=\"" + d.fileName + "\" href=\"javascript:void(0);\" onclick=\"openPreview('" + d.filePath +"',2)\">" + d.fileName + "</a>";
+                            }
+                        }
+                    }}
+                ,{field:'op',align:'center',title:"操作",width:260,templet:function(d){
+                        ////对操作进行初始化
+                        var xml = "<div class=\"layui-btn-group\">";
+                        console.log('<%=admin%>')
+                        console.log((d.canedit != undefined && d.canedit == "1") )
+
+                        if((d.canedit != undefined && d.canedit == "1") || <%=admin%>){
+                            xml +="<a href=\"javascript:void(0)\" onclick=\"checkHandSignatureAndOpen('${ctx}/workstaff/workStaffBasicCertification/form?id="+ d.id +"', '修改')\" class=\"layui-btn layui-btn-xs layui-bg-green\" > 修改</a>";
+                        }
+
+                        if((d.candelete != undefined && d.candelete == "1") || <%=admin%>){
+                            xml +="<a href=\"${ctx}/workstaff/workStaffBasicCertification/delete?id=" + d.id + "\" onclick=\"return confirmx('确认要删除信息吗?', this.href)\"   class=\"layui-btn layui-btn-xs layui-bg-red\"> 删除</a>";
+                        }
+                        xml +="<a href=\"javascript:void(0)\" onclick=\"downloadFile('"+ d.temporaryUrl +"','"+ d.fileName +"')\" class=\"layui-btn layui-btn-xs layui-bg-blue\" > <i class=\"fa fa-download\"></i> 下载</a>";
+
+                        xml+="</div>"
+                        return xml;
+                    }}
+            ]]
+            ,data: [
+                <c:if test="${ not empty page.list}">
+                <c:forEach items="${page.list}" var="workStaffCertificate" varStatus="index">
+                <c:if test="${index.index != 0}">,</c:if>
+                {
+                    "index": "${index.index+1}"
+                    ,
+                    "id": "${workStaffCertificate.id}"
+                    ,
+                    "name": "${workStaffCertificate.name}"
+                    ,
+                    "num": "<c:out value="${workStaffCertificate.num}" escapeXml="true"/>"
+                    ,
+                    'issuingAuthority': "<c:out value="${workStaffCertificate.issuingAuthority}" escapeXml="true"/>"
+                    ,
+                    "issuingDate": "<fmt:formatDate value="${workStaffCertificate.issuingDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "registDate": "<fmt:formatDate value="${workStaffCertificate.registDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "registNum": "${workStaffCertificate.registNum}"
+                    ,
+                    "major": "${workStaffCertificate.major}"
+                    ,
+                    "issType": "${workStaffCertificate.issType}"
+                    ,
+                    "validStartDate": "<fmt:formatDate value="${workStaffCertificate.validStartDate}" pattern="yyyy-MM-dd"/>"
+                    ,
+                    "validEndDate": "<fmt:formatDate value="${workStaffCertificate.validEndDate}" pattern="yyyy-MM-dd"/>"
+
+                    ,"uploadMode": "<c:out value='${workStaffCertificate.uploadMode}'/>"
+                    ,"fileName": "<c:out value='${workStaffCertificate.fileName}'/>"
+                    ,"temporaryUrl": "<c:out value='${workStaffCertificate.temporaryUrl}'/>"
+                    ,"userName": "<c:out value='${workStaffCertificate.userName}'/>"
+                    ,"filePath": "<c:out value='${workStaffCertificate.filePath}'/>"
+                    ,"officeName": "<c:out value='${workStaffCertificate.officeName}'/>"
+
+                    <shiro:hasPermission name="workstaff:workStaffBasicCertification:edit">,
+                    "canedit":
+                        <c:if test="${fns:getUser().id == workStaffCertificate.userId }">"1"</c:if>
+                        <c:if test="${fns:getUser().id != workStaffCertificate.userId }">"0"</c:if>
+                    </shiro:hasPermission>
+                    <shiro:hasPermission name="workstaff:workStaffBasicCertification:del">,
+                    "candelete":
+                        <c:if test="${fns:getUser().id == workStaffCertificate.userId }">"1"</c:if>
+                        <c:if test="${fns:getUser().id != workStaffCertificate.userId }">"0"</c:if>
+                    </shiro:hasPermission>
+                }
+                </c:forEach>
+                </c:if>
+            ]
+        }).config.cols;
+
+        /* /!**
+          * 参数cols是table.render({})中的cols属性值
+          * aop代理是基于event点击事件进行操作的,
+          * 因此cols中务必开启event点击事件!
+          **!/
+         var aopTable = tableEdit.aopObj(cols); //获取一个aop对象
+         /!**
+          * 注意:
+          * 1、 aopTable.on('tool(xxx)',function (obj) {})
+          * 2、 table.on('tool(yyy)',function (obj) {})
+          * 如果1中的xxx与2中的yyy字符串相同时,
+          * 不能同时用,用了会造成后调用者覆盖前调用者。
+          * 应该直接用1来代替2,因为1中包含了2中的事件。
+          * 如果不相同,则可以同时使用。
+          **!/
+         aopTable.on('tool(tableEvent)',function (obj) {
+             var field = obj.field; //单元格字段
+             console.log(field);
+             var value = obj.value; //修改后的值
+             console.log(value);
+             var data = obj.data; //当前行旧数据
+             console.log(data);
+             var event = obj.event; //当前单元格事件属性值
+             console.log(event);
+             var update = {};
+             update[field] = value;
+             //把value更新到行中
+             obj.update(update);
+
+             $.ajax({
+                 type:'post',
+                 url:"${ctx}/workstaff/workStaffBasicInfo/updateAuditUser",
+                data:{"id":data.id,"auditUserId":value.name},
+                dataType:"json",
+                success:function(data){
+                    var data = data;
+                    parent.layer.msg(data.msg,{icon:1});
+                }
+            });
+        });*/
+
+    })
+
+    resizeListTable(130);/*消除由于有竖向滚动条造成table出现横向滚动条*/
+    $("a").on("click",addLinkVisied);
+</script>
+<script>
+    resizeListWindow1();
+    $(window).resize(function(){
+        resizeListWindow1();
+    });
+
+    // 下载文件(通过 fetch 获取 blob,解决跨域时浏览器直接打开而非下载的问题)
+    function downloadFile(fileUrl, fileName) {
+        fetch(fileUrl)
+            .then(function(resp) { return resp.blob(); })
+            .then(function(blob) {
+                var blobUrl = window.URL.createObjectURL(blob);
+                var a = document.createElement('a');
+                a.href = blobUrl;
+                a.download = fileName || 'download';
+                document.body.appendChild(a);
+                a.click();
+                document.body.removeChild(a);
+                window.URL.revokeObjectURL(blobUrl);
+            })
+            .catch(function() {
+                window.open(fileUrl, '_blank');
+            });
+    }
+
+    // 检查手签章是否已上传,通过后再打开弹窗
+    function checkHandSignatureAndOpen(url, title) {
+        $.ajax({
+            url: '${ctx}/workstaff/workStaffBasicCertification/checkHandSignature',
+            type: 'post',
+            dataType: 'json',
+            success: function(data) {
+                if (data.success) {
+                    openDialog(title, url, '95%', '95%');
+                } else {
+                    top.layer.confirm(data.msg || '您尚未上传手签章,请先上传手签章后再进行操作!', {
+                        icon: 5,
+                        title: '提示',
+                        btn: ['去上传手签章', '取消']
+                    }, function(index) {
+                        top.layer.close(index);
+                        top.layer.open({
+                            type: 2,
+                            area: ['600px', '550px'],
+                            title: '上传手签章',
+                            content: '${ctx}/workstaff/workStaffBasicCertification/handSignatureForm',
+                            btn: ['提交', '关闭'],
+                            yes: function(idx, layero) {
+                                var body = top.layer.getChildFrame('body', idx);
+                                var iframeWin = layero.find('iframe')[0];
+                                var inputForm = body.find('#inputForm');
+                                var top_iframe = top.getActiveTab().attr('name');
+                                inputForm.attr('target', top_iframe);
+                                if (iframeWin.contentWindow.doSubmit(idx)) {
+                                    setTimeout(function() {
+                                        top.layer.close(idx);
+                                    }, 100);
+                                }
+                                return false;
+                            }
+                        });
+                    });
+                }
+            },
+            error: function() {
+                top.layer.msg('系统异常,手签章检查失败!', {icon: 5, time: 3000});
+            }
+        });
+    }
+
+    // 点击变更手签章按钮事件
+    $("#exportAll").bind("click", function () {
+        top.layer.open({
+            type: 2,
+            area: ['600px', '550px'],
+            title: "变更手签章",
+            content: "${ctx}/workstaff/workStaffBasicCertification/handSignatureForm",
+            btn: ['提交', '关闭'],
+            yes: function (index, layero) {
+                var body = top.layer.getChildFrame('body', index);
+                var iframeWin = layero.find('iframe')[0];
+                var inputForm = body.find('#inputForm');
+                var top_iframe = top.getActiveTab().attr("name");
+                inputForm.attr("target", top_iframe);
+                if (iframeWin.contentWindow.doSubmit(index)) {
+                    setTimeout(function(){top.layer.close(index)}, 100);
+                }
+                return false;
+            },
+            cancel: function (index) {
+            }
+        });
+    });
+
+</script>
+</body>
+</html>

+ 410 - 0
src/main/webapp/webpage/modules/workstaff/cerification/workStaffBasicCertificationView.jsp

@@ -0,0 +1,410 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/webpage/include/taglib.jsp"%>
+<html>
+<head>
+    <title>发票管理</title>
+    <meta name="decorator" content="default"/>
+    <script type="text/javascript" src="${ctxStatic}/layui/layui.js"></script>
+    <link rel='stylesheet' type="text/css" href="${ctxStatic}/layui/css/layui.css"/>
+    <script src="${ctxStatic}/common/html/js/script.js"></script>
+    <style>
+        #contractTypeDoc-error{
+            top:80px;
+            left:0;
+        }
+        /*超过5个汉字,调整label的长度,以下是配套的*/
+        .layui-item .layui-form-label{
+            width:90px;
+        }
+        .form-group .layui-item .layui-input-block,
+        .query .layui-input-block {
+            margin-left: 116px;
+        }
+        #workInvoiceProjectRelationList td{
+            padding-left: 0px;
+            padding-right: 0px;
+        }
+        .file-upload-section {
+            padding: 10px 0;
+        }
+        .file-upload-section .upload-btn {
+            display: inline-block;
+            padding: 5px 15px;
+            background-color: #1E9FFF;
+            color: #fff;
+            border-radius: 3px;
+            cursor: pointer;
+            font-size: 13px;
+        }
+        .file-upload-section .upload-btn:hover {
+            background-color: #0d8de6;
+        }
+        .file-preview-area {
+            display: inline-block;
+            vertical-align: middle;
+            margin-left: 10px;
+        }
+        .file-preview-area img {
+            vertical-align: middle;
+            border: 1px solid #e6e6e6;
+            border-radius: 3px;
+        }
+        .file-preview-area .file-link {
+            color: #007bff;
+            text-decoration: none;
+            margin-right: 8px;
+        }
+        .file-preview-area .file-link:hover {
+            text-decoration: underline;
+        }
+        .file-preview-area .remove-file-btn {
+            color: #ff5722;
+            cursor: pointer;
+            margin-left: 5px;
+            text-decoration: none;
+        }
+        .file-preview-area .remove-file-btn:hover {
+            text-decoration: underline;
+        }
+    </style>
+    <script type="text/javascript">
+        var validateForm;
+        function doSubmit(obj){//回调函数,在编辑和保存动作时,供openDialog调用提交表单。
+            return false;
+        }
+
+        $(document).ready(function() {
+            var billingContent = $("#billingContent").val();
+            if(8 != billingContent){
+                $(".contractType").hide();
+                $("#contractTypeDoc").attr("class","form-control");
+            }else{
+                $(".contractType").show();
+                $("#contractTypeDoc").attr("class","form-control required");
+            }
+
+
+
+
+            layui.use(['form', 'layer'], function () {
+                var form = layui.form;
+
+                $("#attachment_btn").click(function () {
+                    $("#attachment_file").click();
+                });
+
+            });
+
+            // 页面加载时初始化专业下拉框,传入当前专业值用于回显
+            loadMajorOptions(document.getElementById('name'), '${workStaffCertificate.major}');
+
+            validateForm = $("#inputForm").validate({
+                submitHandler: function(form){
+                    loading('正在提交,请稍等...');
+                    form.submit();
+                },
+                errorContainer: "#messageBox",
+                errorPlacement: function(error, element) {
+
+                    $("#messageBox").text("输入有误,请先更正。");
+                    if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){
+                        error.appendTo(element.parent().parent());
+                    } else {
+                        error.insertAfter(element);
+                    }
+                }
+            });
+
+            laydate.render({
+                elem: '#issuingDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+                , trigger: 'click'
+                , trigger: 'click'
+            });
+            laydate.render({
+                elem: '#registDate', //目标元素。由于laydate.js封装了一个轻量级的选择器引擎,因此elem还允许你传入class、tag但必须按照这种方式 '#id .class'
+                event: 'focus', //响应事件。如果没有传入event,则按照默认的click
+                type : 'date'
+                , trigger: 'click'
+                , trigger: 'click'
+            });
+
+        });
+
+
+
+        function addRow(list, idx, tpl, row){
+            var idx1 = $("#workAccountList tr").length;
+            if(list == '#workAccountList' && idx1 < 100){
+                bornTemplete(list, idx, tpl, row, idx1);
+            }
+        }
+        function bornTemplete(list, idx, tpl, row, idx1){
+            $(list).append(Mustache.render(tpl, {
+                idx: idx, delBtn: true, row: row,
+                order:idx1 + 1
+            }));
+            $(list+idx).find("select").each(function(){
+                $(this).val($(this).attr("data-value"));
+            });
+            $(list+idx).find("input[type='checkbox'], input[type='radio']").each(function(){
+                var ss = $(this).attr("data-value").split(',');
+                for (var i=0; i<ss.length; i++){
+                    if($(this).val() == ss[i]){
+                        $(this).attr("checked","checked");
+                    }
+                }
+            });
+        }
+
+        function delRow(obj, prefix) {
+            var id = $(prefix + "_invoiceId");
+            var delFlag = $(prefix + "_delFlag");
+            if (id.val() == "") {
+                $(obj).parent().parent().remove();
+            } else if (delFlag.val() == "0") {
+                delFlag.val("1");
+                $(obj).html("&divide;").attr("title", "撤回删除");
+                $(obj).parent().parent().addClass("error");
+                $(obj).parent().parent().addClass("hide");
+            } else if (delFlag.val() == "1") {
+                delFlag.val("0");
+                $(obj).html("&times;").attr("title", "删除");
+                $(obj).parent().parent().removeClass("error");
+            }
+            var length=$("#workInvoiceProjectRelationList tr").length;
+            var count=length;
+            for (var i=1;i<=length;i++) {
+                var delFlag = $("#workInvoiceProjectRelationList").find("tr").eq(i-1).find("input").eq(1).val();
+                if (delFlag == "1") {
+                    count =count-1;
+                }
+            }
+            if(count==1){
+                $("#chargeType").val("2")
+                layui.form.render();
+            }else if (count>1){
+                $("#chargeType").val("1")
+                layui.form.render();
+            }else if(count == 0){
+                $("#projectFlag").val("");
+            }
+        }
+
+        function insertTitleCollection(tValue){
+            var list = "${workInvoice.workAttachments}";
+            var size = (list.split('url')).length-1;
+            var files = tValue;
+            for(var i = 0;i<files.length;i++) {
+                var file = files[i];
+                var attachmentId = "";
+                var attachmentFlag = "115";
+                var timestamp = new Date().getTime();
+
+                var storeAs = "workInvoice";
+                var uploadPath = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com/" + storeAs;
+                /*将这段字符串存到数据库即可*/
+                var divId = "_attachment";
+                $("#addFile" + divId).show();
+                multipartUploadWithStsCollection(storeAs, file, attachmentId, attachmentFlag, uploadPath, divId, size);
+            }
+        }
+
+        // 根据证书名称级联加载专业选项
+        // selectedValue: 页面初始化时用于回显当前已选中的专业值
+        function loadMajorOptions(obj, selectedValue){
+            var certificateName = obj.value;
+            var $major = $("#major");
+            $major.html('<option value=""></option>');
+            if (certificateName == "") {
+                return;
+            }
+            $.ajax({
+                type: "post",
+                url: '${ctx}/workstaff/workStaffBasicInfo/getMessage',
+                data: {"type": certificateName},
+                dataType: "json",
+                async: false,
+                success: function(data){
+                    if(data.success) {
+                        for (var info in data.data){
+                            if (selectedValue && data.data[info].value == selectedValue) {
+                                $major.append(new Option(data.data[info].label, data.data[info].value, true, true));
+                            } else {
+                                $major.append(new Option(data.data[info].label, data.data[info].value));
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        // 触发隐藏的文件选择框
+        function triggerFileUpload() {
+            $("#certificateFile").click();
+        }
+
+
+        // 本地文件预览(未上传服务器的blob URL)
+        function previewLocalFile(url) {
+            parent.layer.open({
+                type: 2,
+                area: ['90%', '90%'],
+                title: '文件预览',
+                maxmin: true,
+                shade: 0.2,
+                shadeClose: true,
+                content: url,
+                btn: ['关闭'],
+                cancel: function(index){}
+            });
+        }
+
+        // 下载文件
+        function downloadFile(temporaryUrl, fileName) {
+            var a = document.createElement('a');
+            a.href = temporaryUrl;
+            a.download = fileName || '';
+            a.target = '_blank';
+            document.body.appendChild(a);
+            a.click();
+            document.body.removeChild(a);
+        }
+
+
+    </script>
+</head>
+<body>
+<div class="single-form">
+    <div class="container">
+        <form:form id="inputForm" modelAttribute="workStaffCertificate" action="${ctx}/workstaff/workStaffBasicCertification/view" enctype="multipart/form-data" method="post" class="layui-form">
+            <form:hidden path="id"/>
+
+            <div class="form-group layui-row first">
+                <div class="form-group-label"><h2>证书详情</h2></div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书名称:</label>
+                    <div class="layui-input-block">
+                        <form:input id="name"  path="name" htmlEscape="false" readonly="true" class="form-control required layui-input"/>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书编号:</label>
+                    <div class="layui-input-block">
+                        <form:input id="num"  path="num" htmlEscape="false" readonly="true" class="form-control required layui-input"/>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label">发证机关:</label>
+                    <div class="layui-input-block">
+                        <form:input id="issuingAuthority"  path="issuingAuthority" readonly="true" htmlEscape="false" class="form-control layui-input"/>
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>发证日期:</label>
+                    <div class="layui-input-block">
+                        <input placeholder="请选择发证日期" style="background-color: #f1f1f1" disabled class="laydate-icondate form-control layui-input layer-date required laydate-icon" readonly="readonly" id="issuingDate" name="issuingDate" value="<fmt:formatDate value="${workStaffCertificate.issuingDate}" pattern="yyyy-MM-dd"/>">
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label">注册日期:</label>
+                    <div class="layui-input-block">
+                        <input placeholder="请选择注册日期" style="background-color: #f1f1f1" disabled class="laydate-icondate form-control layui-input layer-date laydate-icon" readonly="readonly" id="registDate" name="registDate" value="<fmt:formatDate value="${workStaffCertificate.registDate}" pattern="yyyy-MM-dd"/>">
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label double-line">注册证书编号:</label>
+                    <div class="layui-input-block">
+                        <form:input id="registNum"  path="registNum" readonly="true" htmlEscape="false" class="form-control layui-input"/>
+                    </div>
+                </div>
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>专业:</label>
+                    <div class="layui-input-block">
+                        <form:input id="major"  path="major" htmlEscape="false" readonly="true" class="form-control required layui-input"/>
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm6">
+                    <label class="layui-form-label"><span class="require-item">*</span>取得方式:</label>
+                    <div class="layui-input-block">
+                        <form:input id="issType"  path="issType" htmlEscape="false" readonly="true" class="form-control required layui-input"/>
+                    </div>
+                </div>
+
+                <div class="layui-item layui-col-sm12 file-upload-section">
+                    <label class="layui-form-label"><span class="require-item">*</span>证书文件:</label>
+                    <div class="layui-input-block">
+                        <div class="file-preview-area" id="filePreviewArea" style="line-height: 40px">
+                            <c:choose>
+                                <c:when test="${not empty workStaffCertificate.temporaryUrl}">
+                                    <c:set var="certFileName" value="${workStaffCertificate.fileName}" />
+                                    <c:set var="certExt" value="${fn:toLowerCase(fn:substringAfter(certFileName, '.'))}" />
+                                    <c:choose>
+                                        <c:when test="${certExt == 'jpg' || certExt == 'jpeg' || certExt == 'png' || certExt == 'gif' || certExt == 'bmp' || certExt == 'webp'}">
+                                            <img src="${workStaffCertificate.temporaryUrl}" width="80" height="80" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')" alt="${fns:abbr(workStaffCertificate.fileName,30)}"/>
+
+                                        </c:when>
+                                        <c:otherwise>
+                                            <c:choose>
+                                                <c:when test="${fn:containsIgnoreCase(workStaffCertificate.fileName,'pdf')}">
+                                                    <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')">${workStaffCertificate.fileName}</a>
+                                                </c:when>
+                                                <c:otherwise>
+                                                    <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%')">${workStaffCertificate.fileName}</a>
+                                                </c:otherwise>
+                                            </c:choose>
+                                        </c:otherwise>
+                                    </c:choose>
+                                    <a href="javascript:void(0);" onclick="downloadFile('${workStaffCertificate.temporaryUrl}','${workStaffCertificate.fileName}')"
+                                       title="下载 ${workStaffCertificate.fileName}"
+                                       style="color:#28a745;margin-left:5px;text-decoration:none;">
+                                        <i class="fa fa-download"></i>
+                                    </a>
+                                </c:when>
+                                <c:otherwise>
+                                    <span style="color:#999;font-size:12px;">未上传文件</span>
+                                </c:otherwise>
+                            </c:choose>
+                        </div>
+                        <span id="originalFileDisplay" style="display:none;">
+                            <c:if test="${not empty workStaffCertificate.temporaryUrl}">
+                                <c:set var="certFileName2" value="${workStaffCertificate.fileName}" />
+                                <c:set var="certExt2" value="${fn:toLowerCase(fn:substringAfter(certFileName2, '.'))}" />
+                                <c:choose>
+                                    <c:when test="${certExt2 == 'jpg' || certExt2 == 'jpeg' || certExt2 == 'png' || certExt2 == 'gif' || certExt2 == 'bmp' || certExt2 == 'webp'}">
+                                        <img src="${workStaffCertificate.temporaryUrl}" width="80" height="80" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')" alt="${fns:abbr(workStaffCertificate.fileName,30)}"/>
+                                    </c:when>
+                                    <c:otherwise>
+                                        <c:choose>
+                                            <c:when test="${fn:containsIgnoreCase(workStaffCertificate.fileName,'pdf')}">
+                                                <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%','1')">${workStaffCertificate.fileName}</a>
+                                            </c:when>
+                                            <c:otherwise>
+                                                <a class="file-link" href="javascript:void(0)" onclick="preview('预览','${workStaffCertificate.temporaryUrl}','90%','90%')">${workStaffCertificate.fileName}</a>
+                                            </c:otherwise>
+                                        </c:choose>
+                                    </c:otherwise>
+                                </c:choose>
+                                <a href="javascript:void(0);" onclick="downloadFile('${workStaffCertificate.temporaryUrl}','${workStaffCertificate.fileName}')"
+                                   title="下载 ${workStaffCertificate.fileName}"
+                                   style="color:#28a745;margin-left:5px;text-decoration:none;">
+                                    <i class="fa fa-download"></i>
+                                </a>
+                            </c:if>
+                        </span>
+                    </div>
+                </div>
+            </div>
+
+
+            <div class="form-group layui-row page-end"></div>
+        </form:form>
+    </div>
+</div>
+
+</body>
+</html>