Selaa lähdekoodia

数电发票添加批量归档功能

徐滕 3 päivää sitten
vanhempi
commit
f94af29faa

+ 22 - 7
src/main/java/com/jeeplus/common/oss/OSSClientUtil.java

@@ -1,13 +1,11 @@
 package com.jeeplus.common.oss;
 
 import com.aliyun.oss.ClientException;
+import com.aliyun.oss.HttpMethod;
 import com.aliyun.oss.OSSClient;
 import com.aliyun.oss.OSSException;
 import com.aliyun.oss.model.*;
 import com.jeeplus.common.config.Global;
-import com.jeeplus.common.utils.Encodes;
-import com.aliyun.oss.HttpMethod;
-import java.util.Base64;
 import com.jeeplus.common.utils.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -846,11 +844,28 @@ public class OSSClientUtil {
 
                 for (OSSObjectSummary summary : objectSummaries) {
                     String ossFileKey = summary.getKey(); // OSS文件完整Key
-                    String fileName = ossFileKey.substring(ossFileKey.lastIndexOf("/") + 1); // 截取文件名
-                    String localFilePath = localTargetDir + fileName; // 本地文件路径
+                    String oldFileName = ossFileKey.substring(ossFileKey.lastIndexOf("/") + 1); // 截取原文件名
+
+                    // ========== 核心修改:1. 处理localSubFolder中的特殊字符 ==========
+                    // 先复制一份localSubFolder,避免修改原参数
+                    String folderNameForFileName = localSubFolder;
+                    // 判断是否包含"所有发票格式",包含则移除
+                    if (folderNameForFileName.contains("所有发票格式")) {
+                        folderNameForFileName = folderNameForFileName.replace("所有发票格式", "");
+                    }
+
+                    // ========== 核心修改:2. 提取原文件后缀 ==========
+                    String fileExt = "";
+                    int lastDotIndex = oldFileName.lastIndexOf(".");
+                    if (lastDotIndex > 0) { // 确保.不是文件名的第一个字符
+                        fileExt = oldFileName.substring(lastDotIndex);
+                    }
+
+                    // ========== 核心修改:3. 拼接新文件名 ==========
+                    String newFileName = folderNameForFileName + fileExt;
 
-                    // 调用你原有单个下载方法
-                    this.downByStreamSaveLocal(ossFileKey, fileName, localFilePath);
+                    String localFilePath = localTargetDir + newFileName; // 本地文件路径
+                    this.downByStreamSaveLocal(ossFileKey, newFileName, localFilePath);
                 }
 
                 // 更新分页标记(2.6.0版本获取下一页标记)

+ 5 - 5
src/main/java/com/jeeplus/modules/workfullmanage/web/WorkFullManageController.java

@@ -34,7 +34,6 @@ import com.jeeplus.modules.sys.service.SystemService;
 import com.jeeplus.modules.sys.service.UserService;
 import com.jeeplus.modules.sys.utils.DictUtils;
 import com.jeeplus.modules.sys.utils.UserUtils;
-import com.jeeplus.modules.workactivity.entity.WorkActivityProcess;
 import com.jeeplus.modules.workactivity.service.WorkActivityProcessService;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientAttachment;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientInfo;
@@ -882,7 +881,7 @@ public class WorkFullManageController extends BaseController {
 	 * 下载附件(文件没有时间戳前缀)
 	 */
 	@RequestMapping("/downLoadOMSInvoiceAttachzip")
-	public void downLoadOMSInvoiceAttachzip(String file, HttpServletResponse response) {
+	public void downLoadOMSInvoiceAttachzip(String file, String fileName, HttpServletResponse response) {
 		// 声明临时变量,用于finally清理
 		File localTargetDirFile = null;
 		File zipFile = null;
@@ -892,7 +891,7 @@ public class WorkFullManageController extends BaseController {
 		try {
 			// 1. 解析OSS Key
 			file = file.replace("amp;", "");
-			String fileName = file.substring(file.lastIndexOf("/") + 1);
+			//fileName = file.substring(file.lastIndexOf("/") + 1);
 			String aliyunUrl = Global.getAliyunUrl();
 			String aliDownloadUrl = Global.getAliDownloadUrl();
 			String cons = "";
@@ -922,7 +921,8 @@ public class WorkFullManageController extends BaseController {
 					? "D:/attachment-file"
 					: "/attachment-file";
 			int lastFolderSlashIndex = folderPath.lastIndexOf("/");
-			String localFolderName = lastFolderSlashIndex == -1 ? folderPath : folderPath.substring(lastFolderSlashIndex + 1);
+			//String localFolderName = lastFolderSlashIndex == -1 ? folderPath : folderPath.substring(lastFolderSlashIndex + 1);
+			String localFolderName = fileName == null || fileName.length() == 0 ? folderPath : fileName;
 			// 本地目标文件夹(下载OSS文件的目录)
 			String localTargetDir = path + File.separator + localFolderName;
 			localTargetDirFile = new File(localTargetDir);
@@ -940,7 +940,7 @@ public class WorkFullManageController extends BaseController {
 			response.setCharacterEncoding("UTF-8");
 			response.setContentType("application/zip");
 			// 处理文件名中文乱码
-			String zipFileName = URLEncoder.encode("编号:" + localFolderName + "所有类型发票.zip", StandardCharsets.UTF_8.name())
+			String zipFileName = URLEncoder.encode(fileName + "所有类型发票.zip", StandardCharsets.UTF_8.name())
 					.replace("+", "%20");
 			response.setHeader("Content-Disposition", "attachment;filename=" + zipFileName);
 

+ 12 - 4
src/main/java/com/jeeplus/modules/workinvoice/dao/WorkInvoiceDao.java

@@ -3,18 +3,18 @@
  */
 package com.jeeplus.modules.workinvoice.dao;
 
+import com.jeeplus.common.persistence.CrudDao;
+import com.jeeplus.common.persistence.annotation.MyBatisDao;
 import com.jeeplus.modules.statement.entity.StatementCompanyComprehensiveInfo;
 import com.jeeplus.modules.workbidproject.entity.WorkBidProject;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientInfo;
-import java.util.List;
-import com.jeeplus.common.persistence.CrudDao;
-import com.jeeplus.common.persistence.annotation.MyBatisDao;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceProjectRelation;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceReceiptInfo;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceTaxClassificationCode;
 import org.apache.ibatis.annotations.Param;
-import org.apache.poi.ss.formula.functions.T;
+
+import java.util.List;
 
 /**
  * 开票管理DAO接口
@@ -288,4 +288,12 @@ public interface WorkInvoiceDao extends CrudDao<WorkInvoice> {
 	 * @return 影响的行数
 	 */
 	int batchUpdateByForeach(@Param("list") List<WorkInvoiceReceiptInfo> list);
+
+
+	/**
+	 * 根据发票申请编号查询数据信息(最近的一条有效数据)
+	 * @param idList
+	 * @return
+	 */
+	List<WorkInvoice> getByIdList(@Param("idList") List<String> idList);
 }

+ 93 - 1
src/main/java/com/jeeplus/modules/workinvoice/service/WorkInvoiceService.java

@@ -7,6 +7,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.jeeplus.common.config.Global;
 import com.jeeplus.common.json.AjaxJson;
+import com.jeeplus.common.oss.OSSClientUtil;
 import com.jeeplus.common.persistence.Page;
 import com.jeeplus.common.service.CrudService;
 import com.jeeplus.common.utils.DateUtils;
@@ -45,11 +46,12 @@ import com.jeeplus.modules.workclientinfo.entity.WorkClientInfo;
 import com.jeeplus.modules.workclientinfo.entity.WorkClientLinkman;
 import com.jeeplus.modules.workcontractinfo.entity.WorkContractInfo;
 import com.jeeplus.modules.workcontractinfo.service.WorkContractInfoService;
+import com.jeeplus.modules.workfullmanage.utils.ZipUtils;
 import com.jeeplus.modules.workinvoice.dao.WorkInvoiceCloudDao;
 import com.jeeplus.modules.workinvoice.dao.WorkInvoiceDao;
 import com.jeeplus.modules.workinvoice.dao.WorkInvoiceReceiptDao;
-import com.jeeplus.modules.workinvoice.entity.*;
 import com.jeeplus.modules.workinvoice.entity.OMS.InvoiceDown.OMSInvoiceDetailInfo;
+import com.jeeplus.modules.workinvoice.entity.*;
 import com.jeeplus.modules.workinvoice.thread.ApprovalThread;
 import com.jeeplus.modules.workinvoice.thread.RedApprovalThread;
 import com.jeeplus.modules.workinvoice.utils.FileHandlingUtil;
@@ -69,6 +71,7 @@ import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.File;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
@@ -4833,4 +4836,93 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 		map.put("moneyMismatchingList", moneyMismatchingList); // 数据表中金额大于开票价的的数据
 		return map;
 	}
+
+
+
+	/**
+	 * 数电发票批量下载(整合OSS单文件下载方法)
+	 * @param idList 发票ID列表
+	 * @return 临时压缩包路径
+	 * @throws Exception 异常抛出给Controller处理
+	 */
+	public String exportInvoiceAll(List<String> idList, String fileName) throws Exception {
+		// 1. 查询发票和附件关联信息(完全保留你原有逻辑)
+		List<WorkInvoice> invoicedList = dao.getByIdList(idList);
+		List<String> invoiceIdList = Lists.newArrayList();
+		for (WorkInvoice workInvoice : invoicedList) {
+			invoiceIdList.add(workInvoice.getId());
+		}
+		List<Workattachment> omsInvoiceFileList = workattachmentService.getByAttachmentIdListAndFlag(invoiceIdList, "OMS_invoice_file");
+
+		// 关联附件URL到发票对象
+		for (WorkInvoice invoice : invoicedList) {
+			for (Workattachment workattachment : omsInvoiceFileList) {
+				if(invoice.getId().equals(workattachment.getAttachmentId())){
+					invoice.setOmsAttachmentUrl(workattachment.getUrl());
+					break;
+				}
+			}
+		}
+
+		// 2. 创建批量下载的根临时文件夹(修改:添加时间戳+UUID,双重防冲突)
+		String rootTempDir = System.getProperty("os.name").toLowerCase().contains("win")
+				? "D:/attachment-file/" + fileName
+				: "/attachment-file/" + fileName;
+		File rootTempDirFile = new File(rootTempDir);
+		if (!rootTempDirFile.exists()) {
+			boolean mkdirsSuccess = rootTempDirFile.mkdirs();
+			if (!mkdirsSuccess) {
+				throw new Exception("创建批量下载临时根文件夹失败:" + rootTempDir);
+			}
+		}
+
+		// 3. 遍历发票,调用OSS下载方法保存到临时文件夹(完全保留你原有逻辑)
+		for (WorkInvoice workInvoice : invoicedList) {
+			String number = workInvoice.getNumber();
+			String widNumber = workInvoice.getWidNumber();
+			String omsAttachmentUrl = workInvoice.getOmsAttachmentUrl();
+			String clientName = workInvoice.getClient() != null ? workInvoice.getClient().getName() : "";
+
+			// 校验必要参数(保留你原有逻辑)
+			if(StringUtils.isBlank(number) || StringUtils.isBlank(widNumber) || StringUtils.isBlank(clientName) || StringUtils.isBlank(omsAttachmentUrl)){
+				logger.warn("发票ID:{} 缺少必要参数,跳过下载", workInvoice.getId());
+				continue;
+			}
+
+			// 3.1 构建发票专属文件夹名称(避免文件名冲突)
+			String invoiceFolderName = "dzfp_" + widNumber + "_" + clientName + "_" + number;
+			String invoiceLocalDir = rootTempDir + File.separator + invoiceFolderName;
+
+			// 3.2 解析OSS URL获取文件夹前缀(适配你的OSS工具类)
+			String aliyunUrl = Global.getAliyunUrl();
+			String aliDownloadUrl = Global.getAliDownloadUrl();
+			String cons = "";
+			if (omsAttachmentUrl.contains(aliyunUrl)) {
+				cons = aliyunUrl;
+			} else if (omsAttachmentUrl.contains("http://gangwan-app.oss-cn-hangzhou.aliyuncs.com")) {
+				cons = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com";
+			} else {
+				cons = aliDownloadUrl;
+			}
+			String ossFolderKey = omsAttachmentUrl.split(cons + "/")[1];
+			// 截取OSS文件夹前缀(去掉文件名,只保留文件夹路径)
+			String ossFolderPrefix = ossFolderKey.substring(0, ossFolderKey.lastIndexOf("/") + 1);
+
+			// 3.3 调用你的OSS批量下载方法(下载该发票的所有文件到专属文件夹)
+			// 注意:response参数此处传null,因为是后台批量下载,无需前端响应
+			new OSSClientUtil().downFolderByStreamSaveLocal(ossFolderPrefix, rootTempDir, invoiceFolderName, null);
+		}
+
+		// 4. 打包整个根临时文件夹为ZIP文件(修改:压缩包名称带时间戳,核心修改2)
+		// 提取临时文件夹根路径(D:/attachment-file 或 /attachment-file)
+		String rootPath = System.getProperty("os.name").toLowerCase().contains("win")
+				? "D:/attachment-file"
+				: "/attachment-file";
+		// 压缩包路径:根路径/数电发票批量下载_时间戳.zip
+		String zipFilePath = rootPath + File.separator + fileName + ".zip";
+		ZipUtils.zipFolder(rootTempDir, zipFilePath);
+
+		// 5. 返回ZIP文件路径(供Controller下载)
+		return zipFilePath;
+	}
 }

+ 3 - 3
src/main/java/com/jeeplus/modules/workinvoice/utils/OMSNationUtil.java

@@ -132,9 +132,9 @@ public class OMSNationUtil {
         omsImportInfo.setBuyerName(workInvoice.getClient().getName());    //购买方名称
         omsImportInfo.setBuyerTaxno(workInvoice.getClient().getUscCode());  //购买方信用代码
         omsImportInfo.setBuyerAddr(workInvoice.getClient().getAddress()); //购买方地址
-        omsImportInfo.setBuyerPhone(workInvoice.getClient().getTelephone()); //购买方电话
-        omsImportInfo.setBuyerBank(workInvoice.getBank()); //购买方开户行名称
-        omsImportInfo.setBuyerBankaccount(workInvoice.getBankNumber()); //购买方开户银行账号
+        omsImportInfo.setBuyerPhone(workInvoice.getClient().getTelephone() != null ? workInvoice.getClient().getTelephone().replaceAll("\\s+", "") : null); //购买方电话
+        omsImportInfo.setBuyerBank(workInvoice.getBank() != null ? workInvoice.getBank().replaceAll("\\s+", "") : null); //购买方开户行名称
+        omsImportInfo.setBuyerBankaccount(workInvoice.getBankNumber() != null ? workInvoice.getBankNumber().replaceAll("\\s+", "") : null); //购买方开户银行账号
         //omsImportInfo.setBuyerNationality(""); //购买方国籍/地区代码
         //omsImportInfo.setBuyerIdcardno(""); //购买方证件号码
         //omsImportInfo.setBuyerIdcartype(""); //购买方证件类型

+ 101 - 10
src/main/java/com/jeeplus/modules/workinvoice/web/WorkInvoiceController.java

@@ -6,9 +6,7 @@ package com.jeeplus.modules.workinvoice.web;
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.jeeplus.common.bos.BOSClientUtil;
 import com.jeeplus.common.config.Global;
-import com.jeeplus.common.json.AjaxJson;
 import com.jeeplus.common.mapper.JsonMapper;
 import com.jeeplus.common.oss.OSSClientUtil;
 import com.jeeplus.common.persistence.Page;
@@ -17,17 +15,13 @@ import com.jeeplus.common.utils.MyBeanUtils;
 import com.jeeplus.common.utils.StringUtils;
 import com.jeeplus.common.utils.excel.ExportExcel;
 import com.jeeplus.common.utils.excel.ImportExcel;
-import com.jeeplus.common.utils.excel.entity.CellModel;
-import com.jeeplus.common.utils.excel.utils.ExcelUtilInvoice;
 import com.jeeplus.common.web.BaseController;
 import com.jeeplus.modules.act.entity.Act;
 import com.jeeplus.modules.act.service.ActTaskService;
 import com.jeeplus.modules.act.utils.ActUtils;
-import com.jeeplus.modules.oa.entity.OaNotify;
 import com.jeeplus.modules.projectrecord.entity.ProjectRecords;
 import com.jeeplus.modules.projectrecord.enums.ProjectStatusEnum;
 import com.jeeplus.modules.projectrecord.service.ProjectRecordsService;
-import com.jeeplus.modules.ruralprojectrecords.entity.RuralProjectRecords;
 import com.jeeplus.modules.ruralprojectrecords.service.RuralProjectRecordsService;
 import com.jeeplus.modules.sys.entity.MainDictDetail;
 import com.jeeplus.modules.sys.entity.Office;
@@ -48,12 +42,12 @@ import com.jeeplus.modules.workclientinfo.entity.WorkClientLinkman;
 import com.jeeplus.modules.workclientinfo.service.WorkClientInfoService;
 import com.jeeplus.modules.workcontractinfo.entity.WorkContractInfo;
 import com.jeeplus.modules.workcontractinfo.service.WorkContractInfoService;
+import com.jeeplus.modules.workfullmanage.utils.ZipUtils;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceProjectRelation;
 import com.jeeplus.modules.workinvoice.service.WorkInvoiceService;
 import com.jeeplus.modules.workinvoicealter.entity.WorkInvoiceAlter;
 import com.jeeplus.modules.workinvoicealter.service.WorkInvoiceAlterService;
-import com.jeeplus.modules.workinvoicedetail.dao.WorkInvoiceDetailDao;
 import com.jeeplus.modules.workinvoicedetail.service.WorkInvoiceDetailService;
 import com.jeeplus.modules.workproject.service.WorkProjectService;
 import com.jeeplus.modules.workprojectnotify.entity.WorkProjectNotify;
@@ -63,9 +57,6 @@ import org.activiti.engine.TaskService;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.activiti.engine.task.Task;
 import org.apache.commons.beanutils.BeanUtils;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.shiro.authz.annotation.Logical;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -82,6 +73,8 @@ import java.io.*;
 import java.lang.reflect.InvocationTargetException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
 import java.util.*;
 
 /**
@@ -1755,4 +1748,102 @@ public class WorkInvoiceController extends BaseController {
 		String path = year+"/"+month+"/"+day + "/";
 		return path;
 	}
+
+
+
+	/**
+	 * 项目相应文件批量下载并压缩
+	 */
+	@RequestMapping(value = "exportInvoiceAll", method = {RequestMethod.GET, RequestMethod.POST}) // 新增POST支持
+	public void exportAll(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) {
+		String listIds = request.getParameter("listId");
+		String showFlag = request.getParameter("showFlag");
+		// 空值校验:避免listIds为空时split报错
+		if (listIds == null || listIds.trim().isEmpty()) {
+			writeErrorResponse(response, "请选择需要下载的发票!");
+			return;
+		}
+
+		List<String> idList = Arrays.asList(listIds.split(","));
+		String zipFilePath = null;
+		InputStream is = null;
+		OutputStream os = null;
+
+		try {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+			String timeStamp = sdf.format(new Date());
+			String fileName = "数电发票批量下载_" + timeStamp;
+
+			// 1. 调用Service生成ZIP文件
+			zipFilePath = workInvoiceService.exportInvoiceAll(idList, fileName);
+
+			// 2. 校验ZIP文件
+			File zipFile = new File(zipFilePath);
+			if (!zipFile.exists() || zipFile.length() == 0) {
+				throw new Exception("批量下载生成的ZIP文件不存在或为空:" + zipFilePath);
+			}
+
+			// 3. 成功:输出文件流(原有逻辑不变)
+			response.setCharacterEncoding("UTF-8");
+			response.setContentType("application/zip");
+			String zipFileName = URLEncoder.encode(fileName + ".zip", StandardCharsets.UTF_8.name())
+					.replace("+", "%20");
+			response.setHeader("Content-Disposition", "attachment;filename=" + zipFileName);
+			response.setHeader("Download-Status", "success"); // 新增:标记成功状态
+
+			// 读取并输出文件
+			is = new BufferedInputStream(new FileInputStream(zipFile));
+			os = new BufferedOutputStream(response.getOutputStream());
+			byte[] buffer = new byte[4096];
+			int len;
+			while ((len = is.read(buffer)) != -1) {
+				os.write(buffer, 0, len);
+			}
+			os.flush();
+			logger.info("批量下载ZIP文件已返回给前端:{}", zipFileName);
+
+		} catch (Exception e) {
+			// 失败:返回JSON格式错误信息
+			logger.error("批量下载数电发票异常:", e);
+			writeErrorResponse(response, "批量下载失败:" + e.getMessage());
+		} finally {
+			// 关闭流 + 清理临时文件(原有逻辑不变)
+			try {
+				if (os != null) os.close();
+				if (is != null) is.close();
+			} catch (IOException e) {
+				logger.error("关闭流失败!", e);
+			}
+
+			if (zipFilePath != null) {
+				File zipFile = new File(zipFilePath);
+				if (zipFile.isFile() && zipFile.exists()) {
+					boolean zipDeleted = zipFile.delete();
+					logger.info("ZIP文件删除{}:{}", zipDeleted ? "成功" : "失败", zipFile.getAbsolutePath());
+				}
+				String tempDirPath = zipFilePath.replace(".zip", "");
+				File tempDirFile = new File(tempDirPath);
+				if (tempDirFile.exists()) {
+					ZipUtils.deleteFolder(tempDirFile);
+					logger.info("批量下载临时文件夹已删除:{}", tempDirPath);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 统一写入错误响应(JSON格式)
+	 */
+	private void writeErrorResponse(HttpServletResponse response, String message) {
+		response.setCharacterEncoding("UTF-8");
+		response.setContentType("application/json;charset=UTF-8"); // 改为JSON格式
+		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+		try {
+			// 输出标准JSON,方便前端解析
+			String errorJson = "{\"code\":500,\"msg\":\"" + message.replace("\"", "\\\"") + "\"}";
+			response.getWriter().write(errorJson);
+		} catch (IOException ioException) {
+			logger.error("响应错误信息失败!", ioException);
+		}
+	}
 }

+ 54 - 54
src/main/resources/jeeplus.properties

@@ -162,7 +162,7 @@ accessKeySecret=arHxB7ZPhizrBYf4844TtyaRctPMgW
 bucketName=xgxm-test
 #\u751F\u4EA7\u73AF\u5883
 #bucketName=xg-ccpm
-#ǩ��bucketName
+#\u01E9\uFFFD\uFFFDbucketName
 qzBucketName=xg-qz
 avatarDir=app-img/avatar/
 notifyDir=app-img/notify/
@@ -297,7 +297,7 @@ remoteServer.uploadMode =2
 #\u6587\u4EF6\u670D\u52A1\u5668IP\u5730\u5740
 remoteServer.visit =
 
-#上报省站接口
+#\u4E0A\u62A5\u7701\u7AD9\u63A5\u53E3
 #reportedUrl = https://comp.jszj.com.cn:8031/api/addProject
 reportedUrl = http://49.77.204.6:10081/JszjQY/rest/addProject
 
@@ -308,127 +308,127 @@ reportedUrl = http://49.77.204.6:10081/JszjQY/rest/addProject
 #
 #signature_http_top = http://121.40.158.10:9182
 #
-##�����templateId
+##\uFFFD\uFFFD\uFFFD\uFFFD\u0123\uFFFD\uFFFDtemplateId
 #vertical_templateId = 2896237810850173018
-##����templateId
+##\uFFFD\uFFFD\uFFFD\u0123\uFFFD\uFFFDtemplateId
 #across_templateId = 2896237585095954500
-##��˾Բ��
+##\uFFFD\uFFFD\u02FE\u0532\uFFFD\uFFFD
 #company_round_seal_id = 2895593387063378002
-##��˾Բ��-��
+##\uFFFD\uFFFD\u02FE\u0532\uFFFD\uFFFD-\uFFFD\uFFFD
 #company_round_seal_id_vertical = 2898042707446829376
-##��˾����
+##\uFFFD\uFFFD\u02FE\uFFFD\uFFFD\uFFFD\uFFFD
 #company_parties_seal_id = 2895597212998434929
-##�󶨵���ӡ����id
+##\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 #approval_category_id = 2895535120981852163
-##(�Զ��壨ҳ��ǩ��)�󶨵���ӡ����id
+##(\uFFFD\u0536\uFFFD\uFFFD\u58E8\u04B3\uFFFD\uFFFD\u01E9\uFFFD\uFFFD)\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 #custom_approval_category_id = 3103876801745744240
-##������ӡ����id
+##\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 #report_category_id = 2896237012158222343
-##���󱨸���ӡ����id
+##\uFFFD\uFFFD\uFFFD\uDB86\uDE38\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 #report_internal_audit_category_id = 3117352569284948928
-##������ӡ����id
+##\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 #judgement_category_id = 2912238663717396786
-##�󶨵���ӡ����id(�γ�)
+##\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid(\uFFFD\u03B3\uFFFD)
 #approval_YC_category_id = 2933219658963918882
-##������ӡ����id(�γ�)
+##\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid(\uFFFD\u03B3\uFFFD)
 #report_YC_category_id = 2933233335469383748
-##�ճ�ǩ������id
+##\uFFFD\u0573\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 #signature_daily_office_work = 3198093533650456821
-##����ǩ������id
+##\uFFFD\uFFFD\uFFFD\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 #signature_project_report_work = 3198093569524338954
-##�󶨵�ǩ������id
+##\uFFFD\uDB9A\uDE35\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 #signature_project_approval_work = 3245294605355073778
 
-#192.168.2.6ǩ�²��Բ���
+#192.168.2.6\u01E9\uFFFD\u00B2\uFFFD\uFFFD\u0532\uFFFD\uFFFD\uFFFD
 apptoken = uIJQmTwyGJ
 appsecret = 2NMBqFigKoInmd43Wohxv5aEDKiiHo
 signature = 232a44ee9ebd251d119f0a65628f678e
 
 signature_http_top = http://192.168.2.130:9182
 
-#�����templateId
+#\uFFFD\uFFFD\uFFFD\uFFFD\u0123\uFFFD\uFFFDtemplateId
 vertical_templateId = 2894156236229259396
-#����templateId
+#\uFFFD\uFFFD\uFFFD\u0123\uFFFD\uFFFDtemplateId
 across_templateId = 2894156210627227768
-#��˾Բ��
+#\uFFFD\uFFFD\u02FE\u0532\uFFFD\uFFFD
 company_round_seal_id = 2894161942659543252
-#��˾Բ��-��
+#\uFFFD\uFFFD\u02FE\u0532\uFFFD\uFFFD-\uFFFD\uFFFD
 company_round_seal_id_vertical = 2898043523878957918
-#��˾����
+#\uFFFD\uFFFD\u02FE\uFFFD\uFFFD\uFFFD\uFFFD
 company_parties_seal_id = 2894163220106129636
-#�󶨵���ӡ����id
+#\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 approval_category_id = 2895582833566102435
-#(�Զ��壨ҳ��ǩ��)�󶨵���ӡ����id
+#(\uFFFD\u0536\uFFFD\uFFFD\u58E8\u04B3\uFFFD\uFFFD\u01E9\uFFFD\uFFFD)\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 custom_approval_category_id = 3103151250849833169
-#������ӡ����id
+#\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 report_category_id = 2895618951099527314
-#���󱨸���ӡ����id
+#\uFFFD\uFFFD\uFFFD\uDB86\uDE38\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 report_internal_audit_category_id = 3116948624000492165
-##������ӡ����id
+##\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid
 judgement_category_id = 2920938119742709765
-#�󶨵���ӡ����id(�γ�)
+#\uFFFD\uDB9A\uDE35\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid(\uFFFD\u03B3\uFFFD)
 approval_YC_category_id = 2932214418853044239
-#������ӡ����id(�γ�)
+#\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04E1\uFFFD\uFFFD\uFFFD\uFFFDid(\uFFFD\u03B3\uFFFD)
 report_YC_category_id = 2933233458312618324
 
-#�ճ�ǩ������id
+#\uFFFD\u0573\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 signature_daily_office_work = 3190218693018722598
 
-#����ǩ������id
+#\uFFFD\uFFFD\uFFFD\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 signature_project_report_work = 3196710189708420066
 
-#�󶨵�ǩ������id
+#\uFFFD\uDB9A\uDE35\uFFFD\u01E9\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDid
 signature_project_approval_work = 3245192847945704016
 
 
 
 
-#����ϵͳ�ĵ�ַ
+#\uFFFD\uFFFD\uFFFD\uFFFD\u03F5\u0373\uFFFD\u0135\uFFFD\u05B7
 CPA_PATH:   http://localhost:2800
-#���ݷֹ�˾�Ƿ�ͬ������
+#\uFFFD\uFFFD\uFFFD\u0777\u05B9\uFFFD\u02FE\uFFFD\u01F7\uFFFD\u036C\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
 SZ_PATH_flag: true
-#���ݷֹ�˾ ��Ʊ
+#\uFFFD\uFFFD\uFFFD\u0777\u05B9\uFFFD\u02FE \uFFFD\uFFFD\u01B1
 SZ_PATH: http://localhost:9527
 #flowable
 SZOA_PATH: http://localhost:9221
-#systemУ���û���Ч
+#system\u0423\uFFFD\uFFFD\uFFFD\u00FB\uFFFD\uFFFD\uFFFD\u0427
 SZUSER_PATH:http://localhost:9213
 
 SZAUTH_PATH:http://127.0.0.17:2800
 
-#������չʾ����ϵͳ������
-#cpa =��
-#Process_1665383385070 ����-��������
+#\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0579\u02BE\uFFFD\uFFFD\uFFFD\uFFFD\u03F5\u0373\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
+#cpa =\uFFFD\uFFFD
+#Process_1665383385070 \uFFFD\uFFFD\uFFFD\uFFFD-\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
 CPA_TASK:   Process_1665383385070
-#Process_1669275081328 ���ݷֹ�˾ ��Ʊ����
+#Process_1669275081328 \uFFFD\uFFFD\uFFFD\u0777\u05B9\uFFFD\u02FE \uFFFD\uFFFD\u01B1\uFFFD\uFFFD\uFFFD\uFFFD
 SZ_TASK: Process_1669275081328
 
-#����������״̬ =�� ��������������л�ȡ�÷����������Ϣ��
+#\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u05F4\u032C =\uFFFD\uFFFD \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u043B\uFFFD\u0221\uFFFD\u00F7\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u03E2\uFFFD\uFFFD
 INQUIRE_STATUS: cpa
 SZCLOUD_STATUS: sz
 
-# ��������
+# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
 publicPassword: Xg@sys9hB2!xWm
 
-#���ݹ�������
+#\uFFFD\uFFFD\uFFFD\u0779\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
 szPublicPassword: Xg@sys9hB2!xWm
 
 
-#OMS开票相关参数
+#OMS\u5F00\u7968\u76F8\u5173\u53C2\u6570
 omsAppId: hyc1
 omsAppKey: hyc1
-#组织编码
+#\u7EC4\u7EC7\u7F16\u7801
 omsDeptCode: 500102204228315131
-#销售方纳税人识别号
+#\u9500\u552E\u65B9\u7EB3\u7A0E\u4EBA\u8BC6\u522B\u53F7
 omsSellerTaxno: 500102204228315131
-#销售方名称
-omsSellerName: 江苏兴光项目管理有限公司
-#销售方银行名称
-omsBankName: 中信银行南京龙江支行
-#销售方银行账号
+#\u9500\u552E\u65B9\u540D\u79F0
+omsSellerName: \u6C5F\u82CF\u5174\u5149\u9879\u76EE\u7BA1\u7406\u6709\u9650\u516C\u53F8
+#\u9500\u552E\u65B9\u94F6\u884C\u540D\u79F0
+omsBankName: \u4E2D\u4FE1\u94F6\u884C\u5357\u4EAC\u9F99\u6C5F\u652F\u884C
+#\u9500\u552E\u65B9\u94F6\u884C\u8D26\u53F7
 omsBankAccount: 7329010182600006811
-#访问接口地址前缀
+#\u8BBF\u95EE\u63A5\u53E3\u5730\u5740\u524D\u7F00
 omsUrl: https://oms-sandbox.einvoice.js.cn:7079
 #omsUrl: https://www.oms.ejinshui-cloud.com:8899
-#用于判定是否开启oms开票流程事件
+#\u7528\u4E8E\u5224\u5B9A\u662F\u5426\u5F00\u542Foms\u5F00\u7968\u6D41\u7A0B\u4E8B\u4EF6
 omsEnabled: false

+ 107 - 0
src/main/resources/mappings/modules/workinvoice/WorkInvoiceDao.xml

@@ -3374,6 +3374,8 @@
 		</where>
 	</select>
 
+
+
 	<select id="getInvoiceNumberStr" resultType="java.lang.String">
 		select group_concat(number) from work_invoice_detail where invoice_id = #{invoiceId}
 	</select>
@@ -3472,4 +3474,109 @@
 		</foreach>
 	</update>
 
+	<select id="getByIdList" resultType="WorkInvoice">
+		SELECT
+		(a.id) AS "id",
+		a.number AS "number",
+		a.process_instance_id AS "processInstanceId",
+		a.client_id AS "client.id",
+		a.project_id AS "project.id",
+		a.project_name AS "projectName",
+		a.money AS "money",
+		a.money AS "moneyStr",
+		a.invoice_type AS "invoiceType",
+		(case when a.invoice_type = '1' then '专票' when a.invoice_type = '2' then '普票' else '' end) as invoiceTypeStr,
+		a.charge_type AS "chargeType",
+		a.content AS "content",
+		a.drawer_id AS "drawer.id",
+		a.office_id AS "office.id",
+		a.invoice_number AS "invoiceNumber",
+		a.invoice_date AS "invoiceDate",
+		a.drawer_name AS "drawerName",
+		a.take_date AS "takeDate",
+		a.invoice_remarks AS "invoiceRemarks",
+		a.is_invoice AS "isInvoice",
+		a.is_charge AS "isCharge",
+		a.is_invalid AS "isInvalid",
+		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.orUnicode AS "orUnicode",
+		a.address AS "address",
+		a.telephone AS "telephone",
+		a.bank AS "bank",
+		a.bank_number AS "bankNumber",
+		a.cancle_reason AS "cancaleReason",
+
+		w.name AS "client.name",
+
+		a.invoice_state AS "invoiceState",
+		w.usc_code AS "client.uscCode",
+		w.fax AS "client.fax",
+		w.or_unicode AS "client.orUnicode",
+		w.telephone AS "client.telephone",
+		a.company_id AS "companyId",
+		a.cancle_reason AS "cancleReason",
+		w.address AS "client.address",
+		a.officee_id AS "officeId",
+		a.province AS "province",
+		a.ext AS "ext",
+		a.billing_content as "billingContent",
+		a.receipt_money_date as "receiptMoneyDate",
+		(case when a.receipt_money = '0' then '否' when a.receipt_money = '1' then '是' when a.receipt_money = '2' then '部分收款' else '否' end) as receiptMoney,
+		a.cancellation_remark as "cancellationRemark",
+		a.account_checking_user_id as "accountCheckingUserId",
+		a.area_id as "area.id",
+		a.new_drawer_id as "newDrawerId",
+		a.new_drawer as "newDrawer",
+		a.actual_drawer_email_address as "actualDrawerEmailAddress",
+		a.actual_drawer_id as "actualDrawerId",
+		a.electronic_invoice_flag as "electronicInvoiceFlag",
+		a.cancellation_process_instance_id as "cancellationProcessInstanceId",
+		a.cancellation_state as "cancellationState",
+		a.related_invoice as "relatedInvoice",
+		a.situation_detail as "situationDetail",
+		a.red_flush_reason as "redFlushReason",
+		a.red_invoice_flag as "redInvoiceFlag",
+		a.red_invoice_relevancy_id as "redInvoiceRelevancyId",
+		a.red_invoice_relevancy_number as "redInvoiceRelevancyNumber",
+		a.oms_access_token_error as "omsAccessTokenError",
+		a.oms_access_token as "omsAccessToken",
+		a.oms_error_message as "omsErrorMessage",
+		a.oms_ein_vno as "omsEinVno",
+		a.oms_ofd_url as "omsOfdUrl",
+		a.oms_pdf_url as "omsPdfUrl",
+		a.oms_xml_url as "omsXmlUrl",
+		a.order_for_goods_result_str as "orderForGoodsResultStr",
+		a.confirmation_slip_result_str as "confirmationSlipResultStr",
+		a.red_flush_reason as "redFlushReason",
+		a.is_sms_notice as "isSmsNotice",
+		a.is_oms_billing as "isOmsBilling"
+
+		,(SELECT wid.number
+		FROM work_invoice_detail wid
+		<where>
+			a.id = wid.invoice_id
+		</where>
+		LIMIT 1) AS widNumber
+
+		FROM work_invoice a
+		LEFT JOIN sys_user su ON su.id = a.create_by
+		LEFT JOIN work_client_info w ON  w.id = a.client_id
+		<where>
+			a.del_flag = 0
+		    and a.invoice_state = 5
+			and is_oms_billing = 0
+			<if test="idList!=null and idList.size!=0">
+				and a.id in
+				<foreach collection="idList" item="id" separator="," open="(" close=")">
+					#{id}
+				</foreach>
+			</if>
+		</where>
+	</select>
+
 </mapper>

+ 80 - 1
src/main/webapp/webpage/modules/workinvoice/workInvoiceAllList.jsp

@@ -668,6 +668,9 @@
 						<shiro:hasPermission name="workInvoiceTwo:workInvoiceTwo:invoiceReport">
 							<a href="javascript:void(0)" style='background-color: #FFB800' onclick="downloadDialogre('开票汇总信息导出', '${ctx}/workinvoiceTwo/workinvoiceTwo/skipDownloadInvoiceForm?view=workInvoiceAll','40%', '400px','','下载,关闭')" class="layui-btn layui-btn-sm layui-bg-blue" > 开票汇总信息导出</a>
 						</shiro:hasPermission>
+
+						<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-red" id="delUser"> 批量下载数电票</button>
+
 						<button class="layui-btn layui-btn-sm layui-bg-green" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
 						<div class=" layui-btn-sm" style="float: right;width: 300px">
 							<span style="color: #999999">金额汇总(开票金额):${sumMoney}(元)</span>
@@ -696,6 +699,7 @@
             ,elem: '#contentTable'
             ,page: false
             ,cols: [[
+				{checkbox: true, fixed: true},
 				//{checkbox: true, fixed: true},
                 {field:'index',align:'center', title: '序号',width:40}
 				,{field:'projName',align:'center', title: '项目名称', minWidth:160,templet:function(d){
@@ -851,8 +855,10 @@
 							// 核心:用JS拼接下载URL,所有动态参数做URL编码(解决中文乱码/特殊字符)
 							var fileUrl = encodeURIComponent(d.omsAttachmentUrl.trim()); // 编码文件url
 							var invoiceNum = d.invoiceNum || ''; // 兼容invoiceNum为null的情况
+							var widNumber = d.widNumber || ''; // 发票号
+							var clientName = d.clientName || ''; // 开票单位
 							// 编码文件名(核心解决中文乱码)
-							var fileName = encodeURIComponent('发票-' + invoiceNum + '所有发票格式.zip');
+							var fileName = encodeURIComponent('dzfp_' + widNumber + '_' + clientName + '_' + invoiceNum + '所有发票格式');
 							// 拼接完整下载地址
 							var downLoadUrl = baseCtx + '/workfullmanage/workFullManage/downLoadOMSInvoiceAttachzip?file=' + fileUrl + '&fileName=' + fileName;
 							// 修正:HTML用class属性(不是className),href拼接转义引号,补充分号
@@ -1018,6 +1024,79 @@
 			// ,height: 315
 		});
 
+		$("#delUser").off('click').on('click', function () {
+			var _this = $(this);
+			// 1. 点击后立即禁用按钮+改文字
+			_this.prop('disabled', true).text('下载中...');
+
+			var checkList = layui.table.checkStatus('contentTable').data;
+			var listId = [];
+			$.each(checkList, function (i, data) {
+				listId.push(data.id);
+			});
+
+			// ========== 新增:最多选择10条限制(核心逻辑) ==========
+			if (listId.length <= 0) {
+				layer.msg("请选择需要下载的发票信息", {icon: 2});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			} else if (listId.length > 10) {
+				layer.msg("最多只能选择10条发票进行批量下载", {icon: 2, time: 3000});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			}
+			// ========== 限制逻辑结束 ==========
+
+			// 2. 显示加载提示(去掉“我知道了”按钮,改为自动关闭)
+			var loadingIndex = layer.msg("文件生成中,下载完成后浏览器会自动弹窗~", {
+				icon:16,
+				time:3000, // 3秒后自动关闭提示框
+				shade:0.1
+			});
+
+			// 3. 创建form+iframe提交请求
+			var randomId = "down_" + new Date().getTime();
+			var downForm = $('<form>', {
+				'action': "${ctx}/workinvoice/workInvoice/exportInvoiceAll",
+				'method': 'POST',
+				'style': 'display:none;'
+			}).appendTo('body');
+			$('<input>', {'type':'hidden','name':'listId','value':listId.join(",")}).appendTo(downForm);
+			var downIframe = $('<iframe>', {'name':randomId,'style':'display:none;'}).appendTo('body');
+			downForm.attr('target', randomId);
+			downForm.submit();
+
+			// 4. 核心:固定延迟+强制恢复按钮(不管下载是否完成,3.5秒后必恢复)
+			setTimeout(function() {
+				cleanResource();
+			}, 3500); // 比提示框多0.5秒,确保体验
+
+			// 5. 兜底:iframe加载完成也恢复(双重保障)
+			downIframe.on('load', function() {
+				try {
+					var iframeDoc = this.contentDocument || this.contentWindow.document;
+					var responseText = iframeDoc.body.textContent || iframeDoc.body.innerText;
+					var res = JSON.parse(responseText);
+					if (res.code === 500) {
+						layer.close(loadingIndex);
+						layer.msg(res.msg, {icon: 2, time: 3000});
+					}
+				} catch (e) {
+					layer.close(loadingIndex);
+					layer.msg("下载已触发,请等待浏览器弹窗~", {icon: 1, time: 2000});
+				}
+				cleanResource(); // 加载完成立即恢复
+			});
+
+			// 统一清理+恢复按钮函数
+			function cleanResource(){
+				downForm.remove();
+				downIframe.remove();
+				// ✅ 强制恢复按钮(核心修复)
+				_this.prop('disabled', false).text('批量下载数电票');
+			}
+		});
+
 	})
 
 	resizeListTable();/*消除由于有竖向滚动条造成table出现横向滚动条*/

+ 81 - 1
src/main/webapp/webpage/modules/workinvoice/workInvoiceAllTwoList.jsp

@@ -747,6 +747,9 @@
 						<shiro:hasPermission name="workInvoiceTwo:workInvoiceTwo:invoiceReport">
 							<a href="javascript:void(0)" style='background-color: #FFB800' onclick="downloadDialogre('开票汇总信息导出', '${ctx}/workinvoiceTwo/workinvoiceTwo/skipDownloadInvoiceForm?view=workInvoiceAllTwo','40%', '400px','','下载,关闭')" class="layui-btn layui-btn-sm layui-bg-blue" > 开票汇总信息导出</a>
 						</shiro:hasPermission>
+
+						<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-red" id="delUser"> 批量下载数电票</button>
+
 						<button class="layui-btn layui-btn-sm layui-bg-green" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
 						<div class=" layui-btn-sm" style="float: right;width: 300px">
 							<span style="color: #999999">金额汇总(开票金额):${sumMoney}(元)</span>
@@ -774,6 +777,7 @@
             ,elem: '#contentTable'
             ,page: false
             ,cols: [[
+				{checkbox: true, fixed: true},
 				{field:'index',align:'center', title: '序号',width:40}
 				,{field:'projName',align:'center', title: '项目名称', minWidth:160,templet:function(d){
 						if(1 == d.showView && d.showView != undefined){
@@ -910,8 +914,10 @@
 							// 核心:用JS拼接下载URL,所有动态参数做URL编码(解决中文乱码/特殊字符)
 							var fileUrl = encodeURIComponent(d.omsAttachmentUrl.trim()); // 编码文件url
 							var invoiceNum = d.invoiceNum || ''; // 兼容invoiceNum为null的情况
+							var widNumber = d.widNumber || ''; // 发票号
+							var clientName = d.clientName || ''; // 开票单位
 							// 编码文件名(核心解决中文乱码)
-							var fileName = encodeURIComponent('发票-' + invoiceNum + '所有发票格式.zip');
+							var fileName = encodeURIComponent('dzfp_' + widNumber + '_' + clientName + '_' + invoiceNum + '所有发票格式');
 							// 拼接完整下载地址
 							var downLoadUrl = baseCtx + '/workfullmanage/workFullManage/downLoadOMSInvoiceAttachzip?file=' + fileUrl + '&fileName=' + fileName;
 							// 修正:HTML用class属性(不是className),href拼接转义引号,补充分号
@@ -1061,6 +1067,80 @@
 			// ,height: 315
 		});
 
+
+		$("#delUser").off('click').on('click', function () {
+			var _this = $(this);
+			// 1. 点击后立即禁用按钮+改文字
+			_this.prop('disabled', true).text('下载中...');
+
+			var checkList = layui.table.checkStatus('contentTable').data;
+			var listId = [];
+			$.each(checkList, function (i, data) {
+				listId.push(data.id);
+			});
+
+			// ========== 新增:最多选择10条限制(核心逻辑) ==========
+			if (listId.length <= 0) {
+				layer.msg("请选择需要下载的发票信息", {icon: 2});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			} else if (listId.length > 10) {
+				layer.msg("最多只能选择10条发票进行批量下载", {icon: 2, time: 3000});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			}
+			// ========== 限制逻辑结束 ==========
+
+			// 2. 显示加载提示(去掉“我知道了”按钮,改为自动关闭)
+			var loadingIndex = layer.msg("文件生成中,下载完成后浏览器会自动弹窗~", {
+				icon:16,
+				time:3000, // 3秒后自动关闭提示框
+				shade:0.1
+			});
+
+			// 3. 创建form+iframe提交请求
+			var randomId = "down_" + new Date().getTime();
+			var downForm = $('<form>', {
+				'action': "${ctx}/workinvoice/workInvoice/exportInvoiceAll",
+				'method': 'POST',
+				'style': 'display:none;'
+			}).appendTo('body');
+			$('<input>', {'type':'hidden','name':'listId','value':listId.join(",")}).appendTo(downForm);
+			var downIframe = $('<iframe>', {'name':randomId,'style':'display:none;'}).appendTo('body');
+			downForm.attr('target', randomId);
+			downForm.submit();
+
+			// 4. 核心:固定延迟+强制恢复按钮(不管下载是否完成,3.5秒后必恢复)
+			setTimeout(function() {
+				cleanResource();
+			}, 3500); // 比提示框多0.5秒,确保体验
+
+			// 5. 兜底:iframe加载完成也恢复(双重保障)
+			downIframe.on('load', function() {
+				try {
+					var iframeDoc = this.contentDocument || this.contentWindow.document;
+					var responseText = iframeDoc.body.textContent || iframeDoc.body.innerText;
+					var res = JSON.parse(responseText);
+					if (res.code === 500) {
+						layer.close(loadingIndex);
+						layer.msg(res.msg, {icon: 2, time: 3000});
+					}
+				} catch (e) {
+					layer.close(loadingIndex);
+					layer.msg("下载已触发,请等待浏览器弹窗~", {icon: 1, time: 2000});
+				}
+				cleanResource(); // 加载完成立即恢复
+			});
+
+			// 统一清理+恢复按钮函数
+			function cleanResource(){
+				downForm.remove();
+				downIframe.remove();
+				// ✅ 强制恢复按钮(核心修复)
+				_this.prop('disabled', false).text('批量下载数电票');
+			}
+		});
+
 	})
 
 	resizeListTable();/*消除由于有竖向滚动条造成table出现横向滚动条*/

+ 81 - 1
src/main/webapp/webpage/modules/workinvoice/workInvoiceTwoList.jsp

@@ -791,6 +791,8 @@
 							<a href="javascript:void(0)" style='background-color: #FFB800' onclick="downloadDialogre('开票汇总信息导出', '${ctx}/workinvoiceTwo/workinvoiceTwo/skipDownloadInvoiceForm?view=workInvoiceTwo','40%', '400px','','下载,关闭')" class="layui-btn layui-btn-sm layui-bg-blue" > 开票汇总信息导出</a>
 						</shiro:hasPermission>
 
+						<button type="button" data-toggle="tooltip" data-placement="top" class="layui-btn layui-btn-sm layui-bg-red" id="delUser"> 批量下载数电票</button>
+
 						<button class="layui-btn layui-btn-sm layui-bg-green" data-toggle="tooltip" data-placement="left" onclick="sortOrRefresh()" title="刷新"> 刷新</button>
 						<div class=" layui-btn-sm" style="float: right;width: 300px">
 							<span style="color: #999999">金额汇总(开票金额):${sumMoney}(元)</span>
@@ -818,6 +820,7 @@
 			,elem: '#contentTable'
 			,page: false
 			,cols: [[
+				{checkbox: true, fixed: true},
 				{field:'index',align:'center', title: '序号',width:40}
 				,{field:'projName',align:'center', title: '项目名称', minWidth:160,templet:function(d){
 						if(1 == d.showView && d.showView != undefined && d.isSzCloud == ''){
@@ -973,8 +976,10 @@
 							// 核心:用JS拼接下载URL,所有动态参数做URL编码(解决中文乱码/特殊字符)
 							var fileUrl = encodeURIComponent(d.omsAttachmentUrl.trim()); // 编码文件url
 							var invoiceNum = d.invoiceNum || ''; // 兼容invoiceNum为null的情况
+							var widNumber = d.widNumber || ''; // 发票号
+							var clientName = d.clientName || ''; // 开票单位
 							// 编码文件名(核心解决中文乱码)
-							var fileName = encodeURIComponent('发票-' + invoiceNum + '所有发票格式.zip');
+							var fileName = encodeURIComponent('dzfp_' + widNumber + '_' + clientName + '_' + invoiceNum + '所有发票格式');
 							// 拼接完整下载地址
 							var downLoadUrl = baseCtx + '/workfullmanage/workFullManage/downLoadOMSInvoiceAttachzip?file=' + fileUrl + '&fileName=' + fileName;
 							// 修正:HTML用class属性(不是className),href拼接转义引号,补充分号
@@ -1125,6 +1130,81 @@
 			// ,height: 315
 		});
 
+
+		$("#delUser").off('click').on('click', function () {
+			var _this = $(this);
+			// 1. 点击后立即禁用按钮+改文字
+			_this.prop('disabled', true).text('下载中...');
+
+			var checkList = layui.table.checkStatus('contentTable').data;
+			var listId = [];
+			$.each(checkList, function (i, data) {
+				listId.push(data.id);
+			});
+
+			// ========== 新增:最多选择10条限制(核心逻辑) ==========
+			if (listId.length <= 0) {
+				layer.msg("请选择需要下载的发票信息", {icon: 2});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			} else if (listId.length > 10) {
+				layer.msg("最多只能选择10条发票进行批量下载", {icon: 2, time: 3000});
+				_this.prop('disabled', false).text('批量下载发票'); // 恢复按钮
+				return;
+			}
+			// ========== 限制逻辑结束 ==========
+
+			// 2. 显示加载提示(去掉“我知道了”按钮,改为自动关闭)
+			var loadingIndex = layer.msg("文件生成中,下载完成后浏览器会自动弹窗~", {
+				icon:16,
+				time:3000, // 3秒后自动关闭提示框
+				shade:0.1
+			});
+
+			// 3. 创建form+iframe提交请求
+			var randomId = "down_" + new Date().getTime();
+			var downForm = $('<form>', {
+				'action': "${ctx}/workinvoice/workInvoice/exportInvoiceAll",
+				'method': 'POST',
+				'style': 'display:none;'
+			}).appendTo('body');
+			$('<input>', {'type':'hidden','name':'listId','value':listId.join(",")}).appendTo(downForm);
+			var downIframe = $('<iframe>', {'name':randomId,'style':'display:none;'}).appendTo('body');
+			downForm.attr('target', randomId);
+			downForm.submit();
+
+			// 4. 核心:固定延迟+强制恢复按钮(不管下载是否完成,3.5秒后必恢复)
+			setTimeout(function() {
+				cleanResource();
+			}, 3500); // 比提示框多0.5秒,确保体验
+
+			// 5. 兜底:iframe加载完成也恢复(双重保障)
+			downIframe.on('load', function() {
+				try {
+					var iframeDoc = this.contentDocument || this.contentWindow.document;
+					var responseText = iframeDoc.body.textContent || iframeDoc.body.innerText;
+					var res = JSON.parse(responseText);
+					if (res.code === 500) {
+						layer.close(loadingIndex);
+						layer.msg(res.msg, {icon: 2, time: 3000});
+					}
+				} catch (e) {
+					layer.close(loadingIndex);
+					layer.msg("下载已触发,请等待浏览器弹窗~", {icon: 1, time: 2000});
+				}
+				cleanResource(); // 加载完成立即恢复
+			});
+
+			// 统一清理+恢复按钮函数
+			function cleanResource(){
+				downForm.remove();
+				downIframe.remove();
+				// ✅ 强制恢复按钮(核心修复)
+				_this.prop('disabled', false).text('批量下载数电票');
+			}
+		});
+
+
 	})
 
 	resizeListTable();/*消除由于有竖向滚动条造成table出现横向滚动条*/