You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

422 lines
19 KiB

8 months ago
4 months ago
4 months ago
8 months ago
8 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
8 months ago
8 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
8 months ago
  1. package com.xujie.sys.common.utils;
  2. import lombok.Setter;
  3. import org.apache.poi.ooxml.POIXMLDocumentPart;
  4. import org.apache.poi.ss.usermodel.*;
  5. import org.apache.poi.xssf.usermodel.*;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.util.*;
  9. import java.util.regex.Matcher;
  10. import java.util.regex.Pattern;
  11. public class ExcelTemplate {
  12. final private static Pattern PATTERN_HDR_FIELD = Pattern.compile("\\$\\{(.*?)}", Pattern.MULTILINE);
  13. final private static Pattern PATTERN_DTL_FIELD = Pattern.compile("#\\{(.*?)}", Pattern.MULTILINE);
  14. private XSSFWorkbook workbook;
  15. private Map<String, Object> variables = new HashMap<>();
  16. private List<Map<String, Object>> listVariables = new ArrayList<>();
  17. // 是否下移形状格式
  18. @Setter
  19. private boolean moveShape = false;
  20. // 是否下移印章(印章是图片)
  21. @Setter
  22. private boolean moveSeal = false;
  23. // 是否设置单元格样式
  24. @Setter
  25. private boolean cellStyle = false;
  26. // 是否设置单元格样式 针对RFID不需要序号定制
  27. @Setter
  28. private boolean cellStyle2 = false;
  29. // 是否设置合并单元格样式
  30. @Setter
  31. private boolean rangeStyle = false;
  32. // 价格靠右 发票
  33. @Setter
  34. private boolean priceRight = false;
  35. // 数字靠右 箱单
  36. @Setter
  37. private boolean intRight = false;
  38. // 报关单
  39. @Setter
  40. private boolean delRight = false;
  41. private ExcelTemplate(){}
  42. public static ExcelTemplate load(InputStream template) throws IOException {
  43. ExcelTemplate excelTemplate = new ExcelTemplate();
  44. excelTemplate.workbook = new XSSFWorkbook(template);
  45. return excelTemplate;
  46. }
  47. public void addVar(String key, Object value){
  48. variables.put(key, value);
  49. }
  50. public void addVarAll(Map values){
  51. variables.putAll(values);
  52. }
  53. public void addListVar(Map<String, Object> row){
  54. listVariables.add(row);
  55. }
  56. public void addListVarAll(Collection rows){
  57. listVariables.addAll(rows);
  58. }
  59. public void clearAll(){
  60. variables.clear();
  61. listVariables.clear();
  62. moveShape = false;
  63. moveSeal = false;
  64. cellStyle = false;
  65. rangeStyle = false;
  66. priceRight = false;
  67. }
  68. private boolean findAndRemoveMergedRegion(XSSFSheet sheet, int rowIndex) {
  69. for (int mi = 0; mi < sheet.getMergedRegions().size(); mi++) {
  70. if (sheet.getMergedRegions().get(mi).getFirstRow() == rowIndex) {
  71. sheet.removeMergedRegion(mi);
  72. return true;
  73. }
  74. }
  75. return false;
  76. }
  77. public XSSFWorkbook render(int index) throws IOException {
  78. XSSFSheet sheet = workbook.getSheetAt(index);
  79. CellCopyPolicy copyPolicy = new CellCopyPolicy();
  80. int dtlRowIndex = -1;
  81. // find detail rows
  82. for (int i = 0; i < sheet.getLastRowNum(); i++) {
  83. XSSFRow row = sheet.getRow(i);
  84. if (row == null) {
  85. continue;
  86. }
  87. for (int j = 0; j < row.getLastCellNum(); j++) {
  88. XSSFCell c = sheet.getRow(i).getCell(j);
  89. if (c == null) {
  90. continue;
  91. }
  92. String cellValue = c.toString();
  93. Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue);
  94. if (matcherDtl.find()) {
  95. dtlRowIndex = i;
  96. break;
  97. }
  98. }
  99. if (dtlRowIndex >= 0) {
  100. break;
  101. }
  102. }
  103. if (dtlRowIndex < sheet.getLastRowNum() && listVariables.size() > 1) {
  104. //循环列表以下区域整体下移
  105. int _rows = sheet.getLastRowNum();
  106. for(int i = _rows; i > dtlRowIndex; i--){
  107. sheet.copyRows(i, i, i + listVariables.size() -1 , copyPolicy);
  108. }
  109. for(int i = 0; i < listVariables.size() - 1; i++) {
  110. int destRowIndex = dtlRowIndex + 1 + i;
  111. sheet.createRow(destRowIndex); //clear destination row
  112. while(findAndRemoveMergedRegion(sheet, destRowIndex)){
  113. continue;
  114. }
  115. sheet.copyRows(dtlRowIndex, dtlRowIndex, destRowIndex, copyPolicy);
  116. }
  117. }
  118. // 遍历所有形状 将列表下方的形状坐标也下移
  119. if (moveShape) {
  120. List<POIXMLDocumentPart> relations = sheet.getRelations();
  121. for (POIXMLDocumentPart part : relations) {
  122. if (part instanceof XSSFDrawing) {
  123. XSSFDrawing drawing = (XSSFDrawing) part;
  124. List<XSSFShape> shapes = drawing.getShapes();
  125. for(int i = 0; i < shapes.size(); i++) {
  126. if ((i==3 || i==5) && shapes.get(i) instanceof XSSFSimpleShape) {
  127. XSSFSimpleShape simpleShape = (XSSFSimpleShape) shapes.get(i);
  128. ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor();
  129. // 调整行坐标实现下移
  130. anchor.setRow1(anchor.getRow1() + listVariables.size() - 1);
  131. anchor.setRow2(anchor.getRow2() + listVariables.size() - 1);
  132. }
  133. if ((i==4 || i==6) && shapes.get(i) instanceof XSSFSimpleShape) {
  134. XSSFSimpleShape simpleShape = (XSSFSimpleShape) shapes.get(i);
  135. ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor();
  136. // 调整行坐标实现下移 因为只占一行 所以不需要调整row2
  137. anchor.setRow1(anchor.getRow1() + listVariables.size() - 1);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. // 遍历所有图片 将列表下方的形状坐标也下移
  144. if (moveSeal) {
  145. List<POIXMLDocumentPart> relations = sheet.getRelations();
  146. for (POIXMLDocumentPart part : relations) {
  147. if (part instanceof XSSFDrawing) {
  148. XSSFDrawing drawing = (XSSFDrawing) part;
  149. List<XSSFShape> shapes = drawing.getShapes();
  150. for (XSSFShape shape : shapes) {
  151. // 只处理图片类型的形状
  152. if (shape instanceof XSSFPicture) {
  153. XSSFPicture picture = (XSSFPicture) shape;
  154. // 调整行坐标实现下移
  155. ClientAnchor anchor = (ClientAnchor) picture.getAnchor();
  156. if (anchor.getRow1() > 10) { // 只下移位于10行之后的图片
  157. anchor.setRow1(anchor.getRow1() + listVariables.size() - 1);
  158. anchor.setRow2(anchor.getRow2() + listVariables.size() - 1);
  159. }
  160. }
  161. }
  162. }
  163. }
  164. }
  165. List<Integer> dtlRows = new ArrayList<>();
  166. //整体填值
  167. for (int i = 0; i <= sheet.getLastRowNum(); i++) {
  168. XSSFRow row = sheet.getRow(i);
  169. if (row == null) {
  170. continue;
  171. }
  172. for (int j = 0; j < row.getLastCellNum(); j++) {
  173. XSSFCell c = sheet.getRow(i).getCell(j);
  174. if (c == null) {
  175. continue;
  176. }
  177. String cellValue = c.toString();
  178. if ("#{pn}".equals(cellValue)) {
  179. dtlRows.add(i);
  180. }
  181. if ("#{levy}".equals(cellValue)) {
  182. dtlRows.add(i);
  183. }
  184. Matcher matcherHdr = PATTERN_HDR_FIELD.matcher(cellValue);
  185. String result = c.getStringCellValue();
  186. while (matcherHdr.find()) {
  187. for (int ri = 1; ri <= matcherHdr.groupCount(); ri++) {
  188. String field = matcherHdr.group(ri);
  189. Object value = variables.getOrDefault(field, "");
  190. result = result.replace(matcherHdr.group(0), String.valueOf(value));
  191. }
  192. if ("${phone1}".equals(cellValue) || "${phone2}".equals(cellValue) || "${hs_code}".equals(cellValue)) {
  193. c.setCellValue(result); // 字符串
  194. } else {
  195. try {
  196. double num = Double.parseDouble(result.replace(",", ""));
  197. c.setCellValue(num); // 数值
  198. } catch (NumberFormatException e) {
  199. c.setCellValue(result); // 字符串
  200. }
  201. }
  202. }
  203. Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue);
  204. while (matcherDtl.find()) {
  205. for (int ri = 1; ri <= matcherDtl.groupCount(); ri++) {
  206. String field = matcherDtl.group(ri);
  207. c.setCellValue(cellValue.replace(matcherDtl.group(0), String.valueOf(listVariables.get(i - dtlRowIndex).getOrDefault(field, ""))));
  208. }
  209. // 设置样式
  210. if (cellStyle) {
  211. XSSFCellStyle style = c.getCellStyle();
  212. style.setBorderBottom(BorderStyle.NONE);
  213. style.setBorderTop(BorderStyle.NONE);
  214. style.setWrapText(true);
  215. style.setAlignment(HorizontalAlignment.LEFT);
  216. c.setCellStyle(style);
  217. if (rangeStyle && j < 4) {
  218. for (int mi = 0; mi < 3; mi++) {
  219. XSSFCell nextc = sheet.getRow(i).getCell(c.getColumnIndex()+mi+1);
  220. if (nextc == null) {
  221. continue;
  222. }
  223. nextc.setCellStyle(style);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. }
  230. // 设置明细样式,因为明细是动态添加的,会有样式和模版偏差,此处主要是第2、3列的样式需要设置
  231. if (cellStyle) {
  232. for (Integer dtlRow : dtlRows) {
  233. XSSFCell c1 = sheet.getRow(dtlRow).getCell(cellStyle2?0:1);
  234. if (c1 == null) {
  235. continue;
  236. }
  237. XSSFCellStyle style = workbook.createCellStyle();
  238. style.setBorderRight(BorderStyle.NONE);
  239. style.setBorderLeft(BorderStyle.MEDIUM); // 实线
  240. style.setBorderBottom(BorderStyle.NONE);
  241. style.setBorderTop(BorderStyle.NONE);
  242. Font font = workbook.createFont();
  243. font.setFontName("Arial"); // 设置字体
  244. font.setFontHeightInPoints((short) 10); // 设置字号
  245. style.setFont(font);
  246. style.setVerticalAlignment(VerticalAlignment.TOP);
  247. c1.setCellStyle(style);
  248. XSSFCell c2 = sheet.getRow(dtlRow).getCell(cellStyle2?1:2);
  249. if (c2 == null) {
  250. continue;
  251. }
  252. XSSFCellStyle style2 = workbook.createCellStyle();
  253. style2.setBorderRight(BorderStyle.MEDIUM);
  254. style2.setBorderLeft(BorderStyle.NONE);
  255. style2.setBorderBottom(BorderStyle.NONE);
  256. style2.setBorderTop(BorderStyle.NONE);
  257. Font font2 = workbook.createFont();
  258. font2.setFontName("Arial"); // 设置字体
  259. font2.setFontHeightInPoints((short) 10); // 设置字号
  260. style2.setFont(font2);
  261. style2.setVerticalAlignment(VerticalAlignment.TOP);
  262. style2.setAlignment(HorizontalAlignment.LEFT);
  263. style2.setWrapText(true);
  264. c2.setCellStyle(style2);
  265. if (priceRight) { //仅供发票excel使用,第8、9是价格列居右
  266. for (int i = 6; i < 9; i++) {
  267. XSSFCell c7 = sheet.getRow(dtlRow).getCell(i);
  268. if (c7 == null) {
  269. continue;
  270. }
  271. // 尝试把字符串转成数值
  272. if (c7.getCellType() == CellType.STRING) {
  273. String strVal = c7.getStringCellValue();
  274. if (strVal != null && !strVal.trim().isEmpty()) {
  275. try {
  276. double num = Double.parseDouble(strVal.replace(",", ""));
  277. c7.setCellValue(num); // 转换为数值写回
  278. } catch (NumberFormatException e) {
  279. // 如果不是数字就保留原字符串
  280. System.out.println("非数字,保持原值: " + strVal);
  281. }
  282. }
  283. }
  284. // 创建样式
  285. XSSFCellStyle style7 = workbook.createCellStyle();
  286. style7.setBorderRight(BorderStyle.MEDIUM);
  287. style7.setBorderLeft(BorderStyle.NONE);
  288. style7.setBorderBottom(BorderStyle.NONE);
  289. style7.setBorderTop(BorderStyle.NONE);
  290. Font font7 = workbook.createFont();
  291. font7.setFontName("Arial");
  292. font7.setFontHeightInPoints((short) 10);
  293. style7.setFont(font7);
  294. style7.setVerticalAlignment(VerticalAlignment.TOP);
  295. style7.setAlignment(HorizontalAlignment.RIGHT);
  296. // 设置千分位格式
  297. DataFormat dataFormat = workbook.createDataFormat();
  298. style7.setDataFormat(i==6?dataFormat.getFormat("#,##0"):
  299. i==7?dataFormat.getFormat("#,##0.00000")
  300. :dataFormat.getFormat("#,##0.00"));
  301. c7.setCellStyle(style7);
  302. }
  303. }
  304. if (intRight) { //仅供箱单excel使用
  305. for (int i = 4; i < 9; i++) {
  306. XSSFCell c7 = sheet.getRow(dtlRow).getCell(i);
  307. if (c7 == null) {
  308. continue;
  309. }
  310. // 尝试把字符串转成数值
  311. if (c7.getCellType() == CellType.STRING) {
  312. String strVal = c7.getStringCellValue();
  313. if (strVal != null && !strVal.trim().isEmpty()) {
  314. try {
  315. double num = Double.parseDouble(strVal.replace(",", ""));
  316. c7.setCellValue(num); // 转换为数值写回
  317. } catch (NumberFormatException e) {
  318. // 如果不是数字就保留原字符串
  319. System.out.println("非数字,保持原值: " + strVal);
  320. }
  321. }
  322. }
  323. // 创建样式
  324. XSSFCellStyle style7 = workbook.createCellStyle();
  325. style7.setBorderRight(BorderStyle.MEDIUM);
  326. style7.setBorderLeft(i==4?BorderStyle.MEDIUM:BorderStyle.NONE);
  327. style7.setBorderBottom(BorderStyle.NONE);
  328. style7.setBorderTop(BorderStyle.NONE);
  329. Font font7 = workbook.createFont();
  330. font7.setFontName("Arial");
  331. font7.setFontHeightInPoints((short) 10);
  332. style7.setFont(font7);
  333. style7.setVerticalAlignment(VerticalAlignment.TOP);
  334. style7.setAlignment(HorizontalAlignment.RIGHT);
  335. // 设置千分位格式
  336. if (i==4 || i==5 || i==8) {
  337. DataFormat dataFormat = workbook.createDataFormat();
  338. style7.setDataFormat(dataFormat.getFormat("#,##0"));
  339. } else {
  340. DataFormat dataFormat = workbook.createDataFormat();
  341. style7.setDataFormat(dataFormat.getFormat("#,##0.00"));
  342. }
  343. c7.setCellStyle(style7);
  344. }
  345. }
  346. }
  347. }
  348. if (delRight) {
  349. for (Integer dtlRow : dtlRows) {
  350. for (int i = 6; i < 12; i++) {
  351. if (i==6 || i==11) {
  352. XSSFCell c7 = sheet.getRow(dtlRow).getCell(i);
  353. if (c7 == null) {
  354. continue;
  355. }
  356. // 尝试把字符串转成数值
  357. if (c7.getCellType() == CellType.STRING) {
  358. String strVal = c7.getStringCellValue();
  359. if (strVal != null && !strVal.trim().isEmpty()) {
  360. try {
  361. double num = Double.parseDouble(strVal.replace(",", ""));
  362. c7.setCellValue(num); // 转换为数值写回
  363. } catch (NumberFormatException e) {
  364. // 如果不是数字就保留原字符串
  365. System.out.println("非数字,保持原值: " + strVal);
  366. }
  367. }
  368. }
  369. // 创建样式
  370. XSSFCellStyle style7 = workbook.createCellStyle();
  371. style7.setBorderRight(BorderStyle.NONE);
  372. style7.setBorderLeft(BorderStyle.NONE);
  373. style7.setBorderBottom(BorderStyle.NONE);
  374. style7.setBorderTop(BorderStyle.NONE);
  375. Font font7 = workbook.createFont();
  376. font7.setFontName("Arial");
  377. font7.setFontHeightInPoints((short) 10);
  378. style7.setFont(font7);
  379. style7.setVerticalAlignment(VerticalAlignment.CENTER);
  380. style7.setAlignment(HorizontalAlignment.RIGHT);
  381. // 设置千分位格式
  382. DataFormat dataFormat = workbook.createDataFormat();
  383. style7.setDataFormat(i==6?dataFormat.getFormat("#,##0"):dataFormat.getFormat("#,##0.00"));
  384. c7.setCellStyle(style7);
  385. }
  386. }
  387. }
  388. }
  389. return workbook;
  390. }
  391. }