diff --git a/README.md b/README.md
index 299b821..8ef5a3d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-7756
-JSD-7756 定时调度邮件推送折叠树默认不展开
\ No newline at end of file
+JSD-7756 定时调度邮件推送折叠树默认不展开\
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..e7bc0a4
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,17 @@
+
+ com.fr.plugin.mail.fold
+
+ yes
+ 1.2
+ 10.0
+ 2018-07-31
+ fr.open
+
+ -->
+ ]]>
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java b/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java
new file mode 100644
index 0000000..07a9e24
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java
@@ -0,0 +1,269 @@
+package com.fr.plugin.mail.fold;
+
+import com.fr.base.DynamicUnitList;
+import com.fr.cache.list.IntList;
+import com.fr.form.ui.Widget;
+import com.fr.main.workbook.ResultWorkBook;
+import com.fr.report.ReportHelper;
+import com.fr.report.cell.ResultCellElement;
+import com.fr.report.cell.WidgetAttrElem;
+import com.fr.report.elementcase.ElementCase;
+import com.fr.report.report.Report;
+import com.fr.report.web.button.form.TreeNodeToggleButton;
+import com.fr.report.worksheet.AnalysisRWorkSheet;
+import com.fr.script.Calculator;
+import com.fr.stable.ViewActor;
+import com.fr.stable.script.CalculatorProvider;
+import com.fr.stable.web.SessionProvider;
+import com.fr.third.v2.org.apache.poi.ss.usermodel.Sheet;
+import com.fr.third.v2.org.apache.poi.ss.usermodel.Workbook;
+import com.fr.third.v2.org.apache.poi.xssf.usermodel.XSSFRow;
+import com.fr.web.core.ReportSessionIDInfor;
+import com.fr.web.core.TreeHTMLWriter;
+import com.fr.web.core.reportcase.WebElementReportCase;
+import com.fr.web.output.html.chwriter.ViewCellWriter;
+import com.fr.web.request.EmptyRepository;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * @author fr.open
+ * @date 2019/6/4
+ */
+public class ExportUtil {
+
+ public static void FoldTree(ResultWorkBook book, Workbook workbook, int i, ReportSessionIDInfor sessionProvider) throws CloneNotSupportedException {
+ book = sessionProvider.getWorkBookDefine().execute(book.getExecuteParameters(), new ViewActor());
+ SessionRepository sessionRepository = new SessionRepository().buildSession(sessionProvider);
+ Sheet poiSheet = workbook.getSheetAt(i);
+ if (book.getReport(i) instanceof AnalysisRWorkSheet) {
+ AnalysisRWorkSheet sheet = (AnalysisRWorkSheet) book.getReport(i);
+ Calculator c = Calculator.createCalculator();
+ c.setAttribute(Report.KEY, sheet);
+ TreeHTMLWriter htmlWriter = new TreeHTMLWriter();
+ ViewCellWriter cellHtmlWriter = new ViewCellWriter(sessionRepository, i, sheet.getReportSettings(), true);
+ htmlWriter.writeReportToHtml(new WebElementReportCase(sheet, sessionRepository), i, cellHtmlWriter, new EmptyRepository(), "");
+ cellHtmlWriter.dealWithAllTreeNodeRelation(c);
+ DynamicUnitList rowHeightList = (DynamicUnitList) ReportHelper.getRowHeightList((ElementCase) book.getReport(i)).clone();
+ List spanList = new ArrayList();
+ for (int j = 0; j < rowHeightList.size(); j++) {
+ if (rowHeightList.get(j).equal_zero()) {
+ spanList.add(j);
+ }
+ }
+ Map map = ExportUtil.generateNodeTree(sheet, spanList);
+ Map map1 = ExportUtil.generateNodeTree(sheet, new ArrayList());
+ if (!map.isEmpty()) {
+ Iterator it = map.keySet().iterator();
+ while (it.hasNext()) {
+ TreeNode node = map.get(it.next());
+ if (node.getParent() == -1) {
+ foldRow(map, node.getSelf(), poiSheet);
+ }
+ }
+ }
+
+ }
+ }
+
+ public static class SessionRepository extends EmptyRepository {
+ private CalculatorProvider calculatorProvider = Calculator.createCalculator();
+
+
+ public SessionRepository buildSession(ReportSessionIDInfor sessionIDInfor) {
+ calculatorProvider.setAttribute(SessionProvider.KEY, sessionIDInfor);
+ return this;
+ }
+
+ @Override
+ public CalculatorProvider getCalculator() {
+ return calculatorProvider;
+ }
+
+ @Override
+ public String getSessionID() {
+ return ((SessionProvider) calculatorProvider.getAttribute(SessionProvider.KEY)).getSessionID();
+ }
+ }
+
+
+ private static void foldRow(Map map, int self, Sheet poiSheet) {
+ TreeNode node = map.get(self);
+ if (node == null) {
+ return;
+ }
+ int start = node.getSons().get(1);
+ int end = node.getSons().get(node.getSons().size() - 1);
+ for (int i = 1; i < node.getSons().size(); i++) {
+ int curStart = getStart(map, node.getSons().get(i));
+ int curEnd = getEnd(map, node.getSons().get(i));
+ if (curStart < start) {
+ start = curStart;
+ }
+ if (curEnd > end) {
+ end = curEnd;
+ }
+ foldRow(map, node.getSons().get(i), poiSheet);
+ }
+
+ poiSheet.groupRow(start, end);
+ if (poiSheet.getRow(self) instanceof XSSFRow) {
+ for (int r = start; r <= end; r++) {
+ XSSFRow expand = (XSSFRow) poiSheet.getRow(r);
+ expand.getCTRow().setHidden(true);
+ }
+ XSSFRow xssfRow = (XSSFRow) poiSheet.getRow(self);
+ xssfRow.getCTRow().setCollapsed(true);
+ }
+ if (node.getParent() == -1) {
+ }
+
+ }
+
+ private static int getStart(Map map, int self) {
+ int start = self;
+ TreeNode node = map.get(self);
+ if (node == null) {
+ return start;
+ }
+ if (node.getSons() == null) {
+ return start;
+ }
+ for (int i = 1; i < node.getSons().size(); i++) {
+ int son = getStart(map, node.getSons().get(i));
+ if (son < start) {
+ start = son;
+ }
+ }
+ return start;
+
+ }
+
+ private static int getEnd(Map map, int self) {
+ int end = self;
+ TreeNode node = map.get(self);
+ if (node == null) {
+ return end;
+ }
+ if (node.getSons() == null) {
+ return end;
+ }
+ for (int i = 1; i < node.getSons().size(); i++) {
+ int son = getEnd(map, node.getSons().get(i));
+ if (son > end) {
+ end = son;
+ }
+ }
+ return end;
+ }
+
+ public static ByteArrayInputStream parse(final OutputStream out) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos = (ByteArrayOutputStream) out;
+ final ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray());
+ return swapStream;
+ }
+
+ private static ResultCellElement findToggleCell(AnalysisRWorkSheet reportCase, int rowIndex) {
+ Iterator cellIterator = reportCase.getRow(rowIndex);
+ while (cellIterator.hasNext()) {
+ ResultCellElement tmpCell = (ResultCellElement) cellIterator.next();
+ if (tmpCell instanceof WidgetAttrElem) {
+ if (((WidgetAttrElem) tmpCell).getWidget() instanceof TreeNodeToggleButton) {
+ return tmpCell;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void buildNodeMap(AnalysisRWorkSheet reportCase, ResultCellElement cellElment, Map nodeMap, int parent, List spanList) {
+ if (cellElment == null) {
+ return;
+ }
+ TreeNodeToggleButton toggleButton = (TreeNodeToggleButton) ((WidgetAttrElem) cellElment).getWidget();
+ int self = cellElment.getRow();
+ IntList sonList = toggleButton.getRelativeIndexList();
+ //折叠行
+ if (sonList != null && sonList.size() > 1) {
+ if (sonList.get(0) == -1) {
+ nodeMap.put(span(self, spanList), new TreeNode(span(self, spanList), span(parent, spanList), span(sonList, spanList)));
+ int size = sonList.size();
+ for (int i = 0; i < size; i++) {
+ buildNodeMap(reportCase, findToggleCell(reportCase, sonList.get(i)), nodeMap, self, spanList);
+ }
+ }
+ }
+ }
+
+ private static IntList span(IntList list, List spanList) {
+ IntList res = new IntList();
+ for (int i = 0; i < list.size(); i++) {
+ res.add(span(list.get(i), spanList));
+ }
+ return res;
+ }
+
+ private static int span(int row, List spanList) {
+ if (spanList.isEmpty()) {
+ return row;
+ }
+ for (int i = 0; i < spanList.size(); i++) {
+ if (row >= spanList.get(i) && (i == spanList.size() - 1 || row < spanList.get(i + 1))) {
+ return row - (i + 1);
+ }
+ }
+ return row;
+ }
+
+ private static void setLevel(Map map) {
+ for (Integer key : map.keySet()) {
+ int level = getLevel(map, map.get(key));
+ TreeNode node = map.get(key);
+ node.setLevel(level);
+ map.put(key, node);
+ }
+ }
+
+ private static int getLevel(Map map, TreeNode treeNode) {
+ if (treeNode.getParent() == -1) {
+ return 0;
+ }
+ int level = 0;
+ int tmp = treeNode.getParent();
+ while (map.get(tmp) != null) {
+ level++;
+ tmp = map.get(tmp).getParent();
+ }
+ return level;
+ }
+
+ public static Map generateNodeTree(AnalysisRWorkSheet resultWS, List spanList) {
+ int rowSize = resultWS.getRowCount();
+ Map nodeMap = new HashMap();
+ //遍历行
+ for (int rowIndex = rowSize; rowIndex > -1; rowIndex--) {
+ ResultCellElement treeNodeCell = findToggleCell(resultWS, rowIndex);
+ if (treeNodeCell != null) {
+ Widget widget = ((WidgetAttrElem) treeNodeCell).getWidget();
+ IntList sonList = ((TreeNodeToggleButton) widget).getRelativeIndexList();
+ if (sonList != null && sonList.size() > 1) {
+ if (sonList.get(0) == -1) {
+ //折叠行
+ if (nodeMap.containsKey(span(treeNodeCell.getRow(),spanList))) {
+ continue;
+ }
+ buildNodeMap(resultWS, treeNodeCell, nodeMap, -1, spanList);
+ setLevel(nodeMap);
+ } else {
+ //折叠列 暂不处理
+ }
+ }
+ }
+ }
+ return nodeMap;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java b/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java
new file mode 100644
index 0000000..d2889df
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java
@@ -0,0 +1,38 @@
+package com.fr.plugin.mail.fold;
+
+import com.fr.stable.html.Tag;
+
+import java.util.List;
+
+/**
+ * @author fr.open
+ * @version 10.0
+ * Created by fr.open on 2020/7/6
+ */
+public class JoinedTag extends Tag {
+
+ public JoinedTag(Tag originalTag) {
+ super.setTagName( originalTag.getTagName());
+ super.setId(originalTag.getId());
+ super.setClasses(originalTag.getClasses());
+ super.setStyles(originalTag.getStyles());
+ super.setAttributes(originalTag.getAttributes());
+ super.setSubHtmlList(originalTag.getSubHtmlList());
+ super.setSiblingHtmlList(originalTag.getSiblingHtmlList());
+ }
+
+ @Override
+ public StringBuffer getClassList() {
+ StringBuffer sb = new StringBuffer();
+ List classes = super.getClasses();
+ if (classes != null && classes.size() > 0) {
+ int size = classes.size();
+ for (int i = 0; i < size - 1; i++) {
+ sb.append(classes.get(i));
+ sb.append(' ');
+ }
+ sb.append(classes.get(size - 1));
+ }
+ return new StringBuffer(sb.toString().replace(" ", ""));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java b/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java
new file mode 100644
index 0000000..665d0b3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java
@@ -0,0 +1,79 @@
+package com.fr.plugin.mail.fold;
+
+import com.fr.main.workbook.ResultWorkBook;
+import com.fr.plugin.transform.FunctionRecorder;
+import com.fr.report.cell.ResultCellElement;
+import com.fr.report.cell.WidgetAttrElem;
+import com.fr.report.web.button.form.TreeNodeToggleButton;
+import com.fr.report.worksheet.AnalysisRWorkSheet;
+import com.fr.schedule.base.bean.output.OutputEmail;
+import com.fr.schedule.base.constant.ScheduleConstants;
+import com.fr.schedule.extension.report.job.output.formula.EmailFormula;
+import com.fr.schedule.extension.report.util.ScheduleParameterUtils;
+import com.fr.schedule.feature.util.ScheduleUtils;
+import com.fr.stable.unit.UNIT;
+import com.fr.web.core.ReportSessionIDInfor;
+
+import java.util.*;
+
+/**
+ * @Author fr.open
+ * @Date 2021/5/8
+ * @Description
+ **/
+@FunctionRecorder
+public class MailFoldFormula extends EmailFormula {
+ @Override
+ public void dealWithFormulaParam(OutputEmail outputEmail, ResultWorkBook resultWorkBook, List