فهرست منبع

知识库,数据导出

徐滕 3 هفته پیش
والد
کامیت
a32d644bab

+ 249 - 0
src/main/java/com/jeeplus/common/utils/excel/ExportKnowledgeBaseExcel.java

@@ -0,0 +1,249 @@
+package com.jeeplus.common.utils.excel;
+
+import com.jeeplus.modules.sys.utils.DictUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.text.SimpleDateFormat;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import com.jeeplus.common.utils.excel.annotation.ExcelField;
+
+/**
+ * 知识库多页签Excel导出工具类
+ * 基于ExportMultipleTabsExcel,针对知识库导出场景做了以下定制:
+ * 1. 表头格式为 "中文名|mapKey",导出时表头行只显示中文名部分
+ * 2. 序号列(第一列)使用较窄的列宽
+ */
+public class ExportKnowledgeBaseExcel {
+	private static final Logger log = LoggerFactory.getLogger(ExportKnowledgeBaseExcel.class);
+	private Workbook workbook;
+	private List<MapSheetData> mapSheetDataList = new ArrayList<>();
+
+	/**
+	 * 内部类:存储单个Sheet的数据(Map模式)
+	 */
+	private static class MapSheetData {
+		String sheetName;
+		String title;
+		List<String> headers;
+		List<Map<String, Object>> dataList;
+
+		public MapSheetData(String sheetName, String title, List<String> headers, List<Map<String, Object>> dataList) {
+			this.sheetName = sheetName;
+			this.title = title;
+			this.headers = headers;
+			this.dataList = dataList;
+		}
+	}
+
+	/**
+	 * 构造函数:初始化工作簿
+	 */
+	public ExportKnowledgeBaseExcel() {
+		this.workbook = new SXSSFWorkbook(1000);
+	}
+
+	/**
+	 * 添加一个Sheet页(Map模式,使用自定义表头和Map数据)
+	 * 表头格式为 "中文名|mapKey",导出时表头只显示中文名,数据按mapKey取值
+	 */
+	public void addSheet(String sheetName, String title, List<String> headers, List<Map<String, Object>> dataList) {
+		mapSheetDataList.add(new MapSheetData(sheetName, title, headers, dataList));
+	}
+
+	/**
+	 * 创建单元格样式
+	 */
+	private Map<String, CellStyle> createStyles(Workbook wb) {
+		Map<String, CellStyle> styles = new HashMap<>();
+
+		// 标题样式
+		CellStyle style = wb.createCellStyle();
+		style.setAlignment(HorizontalAlignment.CENTER);
+		style.setVerticalAlignment(VerticalAlignment.CENTER);
+		style.setBorderBottom(BorderStyle.THIN);
+		style.setBorderLeft(BorderStyle.THIN);
+		style.setBorderRight(BorderStyle.THIN);
+		style.setBorderTop(BorderStyle.THIN);
+		Font titleFont = wb.createFont();
+		titleFont.setFontName("宋体");
+		titleFont.setFontHeightInPoints((short) 16);
+		titleFont.setBold(true);
+		style.setFont(titleFont);
+		styles.put("title", style);
+
+		// 表头样式(无边框 + 更深底色 + 白色字体)
+		style = wb.createCellStyle();
+		style.setAlignment(HorizontalAlignment.CENTER);
+		style.setVerticalAlignment(VerticalAlignment.CENTER);
+		style.setBorderBottom(BorderStyle.NONE);
+		style.setBorderLeft(BorderStyle.NONE);
+		style.setBorderRight(BorderStyle.NONE);
+		style.setBorderTop(BorderStyle.NONE);
+		style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+		Font headerFont = wb.createFont();
+		headerFont.setFontName("宋体");
+		headerFont.setFontHeightInPoints((short) 11);
+		headerFont.setBold(true);
+		headerFont.setColor(IndexedColors.WHITE.getIndex());
+		style.setFont(headerFont);
+		styles.put("header", style);
+
+		// 字符串居中样式
+		CellStyle stringStyle = wb.createCellStyle();
+		stringStyle.setAlignment(HorizontalAlignment.CENTER);
+		stringStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+		stringStyle.setBorderBottom(BorderStyle.THIN);
+		stringStyle.setBorderLeft(BorderStyle.THIN);
+		stringStyle.setBorderRight(BorderStyle.THIN);
+		stringStyle.setBorderTop(BorderStyle.THIN);
+		Font stringFont = wb.createFont();
+		stringFont.setFontName("宋体");
+		stringFont.setFontHeightInPoints((short) 11);
+		stringStyle.setFont(stringFont);
+		styles.put("data_string", stringStyle);
+
+		// 数字靠右样式
+		CellStyle numberStyle = wb.createCellStyle();
+		numberStyle.setAlignment(HorizontalAlignment.RIGHT);
+		numberStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+		numberStyle.setBorderBottom(BorderStyle.THIN);
+		numberStyle.setBorderLeft(BorderStyle.THIN);
+		numberStyle.setBorderRight(BorderStyle.THIN);
+		numberStyle.setBorderTop(BorderStyle.THIN);
+		Font numberFont = wb.createFont();
+		numberFont.setFontName("宋体");
+		numberFont.setFontHeightInPoints((short) 11);
+		numberStyle.setFont(numberFont);
+		styles.put("data_number", numberStyle);
+
+		return styles;
+	}
+
+	/**
+	 * 生成Excel文件
+	 */
+	public void write(OutputStream os) throws IOException {
+		for (MapSheetData sheetData : mapSheetDataList) {
+			createMapSheetContent(sheetData);
+		}
+		workbook.write(os);
+	}
+
+	/**
+	 * 创建Map模式Sheet的内容
+	 * 表头格式为 "中文名|mapKey":表头行只显示中文名,数据按mapKey从Map中取值
+	 */
+	private void createMapSheetContent(MapSheetData sheetData) {
+		Sheet sheet = workbook.createSheet(sheetData.sheetName);
+		Map<String, CellStyle> styles = createStyles(workbook);
+		List<String> headers = sheetData.headers;
+		int rownum = 0;
+
+		// 创建标题行
+		if (StringUtils.isNotBlank(sheetData.title)) {
+			Row titleRow = sheet.createRow(rownum++);
+			titleRow.setHeightInPoints(30);
+			Cell titleCell = titleRow.createCell(0);
+			titleCell.setCellStyle(styles.get("title"));
+			titleCell.setCellValue(sheetData.title);
+			sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headers.size() - 1));
+		}
+
+		// 创建表头行
+		Row headerRow = sheet.createRow(rownum++);
+		headerRow.setHeightInPoints(18);
+		for (int i = 0; i < headers.size(); i++) {
+			String header = headers.get(i);
+			// 表头只显示"|"前的中文名称
+			String displayHeader = header.contains("|") ? header.split("\\|")[0] : header;
+			int width = 0;
+			for (char c : displayHeader.toCharArray()) {
+				width += (c > 127) ? 2 : 1;
+			}
+			width = Math.max(width, 10);
+			width = Math.min(width, 50);
+			// 序号列限制较窄宽度(一般三位数即可)
+			if (i == 0) {
+				width = Math.min(width, 5);
+			}
+			sheet.setColumnWidth(i, width * 256 * 2);
+
+			Cell headerCell = headerRow.createCell(i);
+			headerCell.setCellStyle(styles.get("header"));
+			headerCell.setCellValue(displayHeader);
+		}
+
+		// 填充数据行
+		if (sheetData.dataList != null && !sheetData.dataList.isEmpty()) {
+			for (Map<String, Object> dataMap : sheetData.dataList) {
+				Row dataRow = sheet.createRow(rownum++);
+				dataRow.setHeightInPoints(16);
+				for (int i = 0; i < headers.size(); i++) {
+					String header = headers.get(i);
+					// 从header提取map key(格式:header|key 或纯header)
+					String mapKey = header.contains("|") ? header.split("\\|")[1] : header;
+					Object value = dataMap.get(mapKey);
+
+					Cell cell = dataRow.createCell(i);
+					if (value instanceof Number) {
+						cell.setCellStyle(styles.get("data_number"));
+					} else {
+						cell.setCellStyle(styles.get("data_string"));
+					}
+					setMapCellValue(cell, value);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 设置Map模式单元格值
+	 */
+	private void setMapCellValue(Cell cell, Object value) {
+		if (value == null) {
+			cell.setCellValue("");
+			return;
+		}
+		if (value instanceof Date) {
+			cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format((Date) value));
+		} else if (value instanceof Number) {
+			cell.setCellValue(((Number) value).doubleValue());
+		} else if (value instanceof Boolean) {
+			cell.setCellValue((Boolean) value);
+		} else {
+			cell.setCellValue(value.toString());
+		}
+	}
+
+	/**
+	 * 输出到客户端下载
+	 */
+	public void write(HttpServletResponse response, String fileName) throws IOException {
+		response.reset();
+		response.setContentType("application/octet-stream; charset=utf-8");
+		response.setHeader("Content-Disposition", "attachment; filename="
+				+ new String(fileName.getBytes("utf-8"), "ISO-8859-1"));
+		write(response.getOutputStream());
+	}
+
+	/**
+	 * 清理临时文件(避免内存泄漏)
+	 */
+	public void dispose() {
+		if (workbook instanceof SXSSFWorkbook) {
+			((SXSSFWorkbook) workbook).dispose();
+		}
+	}
+}

+ 10 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/dao/WorkKnowledgeBaseTreeInfoDao.java

@@ -44,4 +44,14 @@ public interface WorkKnowledgeBaseTreeInfoDao extends CrudDao<WorkKnowledgeBaseT
      * 查询所有根节点(parent_id='0'的节点)
      */
     List<WorkKnowledgeBaseTreeInfo> findRootNodes();
+
+    /**
+     * 根据父节点ID查询直接子节点
+     */
+    List<WorkKnowledgeBaseTreeInfo> findDirectChildrenByParentId(@Param("parentId") String parentId);
+
+    /**
+     * 根据根节点ID查询所有子孙节点(不含根节点本身)
+     */
+    List<WorkKnowledgeBaseTreeInfo> findAllDescendantsByRootId(@Param("rootId") String rootId);
 }

+ 51 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/service/WorkKnowledgeBaseShareService.java

@@ -224,6 +224,57 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
     }
 
     /**
+     * 动态列表查询全部数据(不分页,用于导出)
+     * 复用 findDynamicList SQL,不传 offset/limit
+     */
+    public List<Map<String, Object>> findDynamicListAll(WorkKnowledgeBaseShareInfo entity,
+                                                         List<WorkKnowledgeBaseDynamicInfo> dynamicFields,
+                                                         Map<String, String> dynamicConditions) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("entity", entity);
+        params.put("dynamicFields", dynamicFields);
+        params.put("dynamicConditions", dynamicConditions);
+        // 不传 offset/limit,SQL不会加LIMIT,查询全部数据
+        return dao.findDynamicList(params);
+    }
+
+    /**
+     * 动态列表查询全部数据(不分页,按指定节点ID列表查询,用于导出)
+     * 支持传入多个treeNodeId,查询这些节点关联的数据
+     */
+    public List<Map<String, Object>> findDynamicListAllByTreeNodeIds(List<String> treeNodeIds,
+                                                                      WorkKnowledgeBaseShareInfo entity,
+                                                                      List<WorkKnowledgeBaseDynamicInfo> dynamicFields,
+                                                                      Map<String, String> dynamicConditions) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("entity", entity);
+        params.put("treeNodeIds", treeNodeIds);
+        params.put("dynamicFields", dynamicFields);
+        params.put("dynamicConditions", dynamicConditions);
+        return dao.findDynamicList(params);
+    }
+
+    /**
+     * 根据父节点ID查询直接子节点(用于多页签导出获取大类列表)
+     */
+    public List<WorkKnowledgeBaseTreeInfo> findDirectChildrenByParentId(String parentId) {
+        if (StringUtils.isBlank(parentId)) {
+            return new ArrayList<>();
+        }
+        return treeInfoDao.findDirectChildrenByParentId(parentId);
+    }
+
+    /**
+     * 根据根节点ID查询所有子孙节点(不含根节点本身,用于多页签导出)
+     */
+    public List<WorkKnowledgeBaseTreeInfo> findAllDescendantsByRootId(String rootId) {
+        if (StringUtils.isBlank(rootId)) {
+            return new ArrayList<>();
+        }
+        return treeInfoDao.findAllDescendantsByRootId(rootId);
+    }
+
+    /**
      * 根据根节点id查询文件信息(不含动态字段,用于表单回显基础信息)
      */
     public WorkKnowledgeBaseShareInfo getWithDynamicValues(String id) {

+ 151 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/web/WorkKnowledgeBaseShareController.java

@@ -6,6 +6,7 @@ import com.jeeplus.common.persistence.Page;
 import com.jeeplus.common.utils.DateUtils;
 import com.jeeplus.common.utils.StringUtils;
 import com.jeeplus.common.utils.excel.ExportExcel;
+import com.jeeplus.common.utils.excel.ExportKnowledgeBaseExcel;
 import com.jeeplus.common.utils.excel.ImportExcel;
 import com.jeeplus.common.web.BaseController;
 import com.jeeplus.modules.WorkKnowledgeBase.entity.*;
@@ -27,8 +28,10 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * 知识库-文件信息管理 Controller
@@ -522,6 +525,154 @@ public class WorkKnowledgeBaseShareController extends BaseController {
         }
     }
 
+    /**
+     * 多页签导出:每个根节点(parent_id=0)作为一个Sheet页签
+     * 该根节点下所有子孙节点的数据全部放在同一个Sheet中,
+     * 最后一列标注数据来自哪个子节点
+     */
+    @RequiresPermissions("workKnowledgeBase:share:export")
+    @RequestMapping(value = "exportAllTabs")
+    public void exportAllTabs(WorkKnowledgeBaseShareInfo shareInfo,
+                               HttpServletRequest request, HttpServletResponse response) {
+        try {
+            // 获取所有根节点(parent_id='0')
+            List<WorkKnowledgeBaseTreeInfo> rootNodes = shareService.findRootNodes();
+            if (rootNodes == null || rootNodes.isEmpty()) {
+                throw new RuntimeException("没有找到知识库根节点");
+            }
+
+            // 收集固定筛选参数
+            String name = request.getParameter("name");
+            String beginDate = request.getParameter("beginDate");
+            String endDate = request.getParameter("endDate");
+
+            // 加载所有树节点,构建 nodeId -> treeName 映射(用于标注所属子节点)
+            Map<String, String> nodeNameMap = new HashMap<>();
+            for (WorkKnowledgeBaseTreeInfo rn : rootNodes) {
+                nodeNameMap.put(rn.getId(), rn.getTreeName());
+                List<WorkKnowledgeBaseTreeInfo> descendants = shareService.findAllDescendantsByRootId(rn.getId());
+                if (descendants != null) {
+                    for (WorkKnowledgeBaseTreeInfo n : descendants) {
+                        nodeNameMap.put(n.getId(), n.getTreeName());
+                    }
+                }
+            }
+
+            ExportKnowledgeBaseExcel excel = new ExportKnowledgeBaseExcel();
+            Set<String> usedSheetNames = new HashSet<>();
+
+            // 为每个根节点创建一个Sheet
+            for (WorkKnowledgeBaseTreeInfo rootNode : rootNodes) {
+                String rootId = rootNode.getId();
+
+                // 获取该根节点配置的动态字段
+                List<WorkKnowledgeBaseDynamicInfo> rootDynamicFields = shareService.findDynamicFields(rootId);
+                if (rootDynamicFields == null) {
+                    rootDynamicFields = new ArrayList<>();
+                }
+
+                // 收集该根节点的动态字段筛选条件
+                Map<String, String> dynamicConditions = new HashMap<>();
+                for (WorkKnowledgeBaseDynamicInfo field : rootDynamicFields) {
+                    String paramValue = request.getParameter("dynamic_" + field.getId());
+                    if (StringUtils.isNotBlank(paramValue)) {
+                        dynamicConditions.put(field.getId(), paramValue.trim());
+                    }
+                }
+
+                // 构建导出列:序号 + 固定列 + 动态列 + 所属子节点
+                List<String> exportHeaders = new ArrayList<>();
+                exportHeaders.add("序号|_rowNum");
+                exportHeaders.add("文件名称|name");
+                exportHeaders.add("文件地址|fileName");
+                for (WorkKnowledgeBaseDynamicInfo field : rootDynamicFields) {
+                    exportHeaders.add(field.getFieldLabel() + "|dynamic_" + field.getFieldKey());
+                }
+                exportHeaders.add("内容属性|contentAttribute");
+                exportHeaders.add("创建人|createByName");
+                exportHeaders.add("创建时间|createTime");
+                exportHeaders.add("状态|statusLabel");
+                exportHeaders.add("所属子节点|childNodeName");
+
+                // 查询该根节点下所有数据(通过rootId过滤)
+                WorkKnowledgeBaseShareInfo queryEntity = new WorkKnowledgeBaseShareInfo();
+                queryEntity.setRootId(rootId);
+                if (StringUtils.isNotBlank(name)) {
+                    queryEntity.setName(name);
+                }
+                queryEntity.setBeginDate(beginDate);
+                queryEntity.setEndDate(endDate);
+                List<Map<String, Object>> dataList = shareService.findDynamicListAll(queryEntity, rootDynamicFields, dynamicConditions);
+
+                // 处理导出数据
+                List<Map<String, Object>> exportData = new ArrayList<>();
+                int rowNum = 1;
+                for (Map<String, Object> row : dataList) {
+                    Map<String, Object> exportRow = new HashMap<>(row);
+                    exportRow.put("_rowNum", rowNum++);
+                    // 状态映射
+                    String auditStatus = row.get("auditStatus") != null ? row.get("auditStatus").toString() : "";
+                    String statusLabel;
+                    switch (auditStatus) {
+                        case "0": statusLabel = "草稿"; break;
+                        case "1": statusLabel = "未审核"; break;
+                        case "2": statusLabel = "审核中"; break;
+                        case "4": statusLabel = "审核未通过"; break;
+                        case "5": statusLabel = "审核通过"; break;
+                        case "6": statusLabel = "待回答"; break;
+                        case "7": statusLabel = "回答中"; break;
+                        case "8": statusLabel = "已结束"; break;
+                        default: statusLabel = ""; break;
+                    }
+                    exportRow.put("statusLabel", statusLabel);
+                    // 内容属性转换
+                    String contentAttr = row.get("contentAttribute") != null ? row.get("contentAttribute").toString() : "";
+                    if ("0".equals(contentAttr)) {
+                        exportRow.put("contentAttribute", "原创");
+                    } else if ("1".equals(contentAttr)) {
+                        exportRow.put("contentAttribute", "转载");
+                    } else {
+                        exportRow.put("contentAttribute", "");
+                    }
+                    // 所属子节点名称
+                    String treeNodeId = row.get("treeNodeId") != null ? row.get("treeNodeId").toString() : "";
+                    String childNodeName = nodeNameMap.getOrDefault(treeNodeId, "");
+                    exportRow.put("childNodeName", childNodeName);
+
+                    exportData.add(exportRow);
+                }
+
+                // Sheet名称处理
+                String sheetName = rootNode.getTreeName();
+                if (sheetName == null || sheetName.isEmpty()) {
+                    sheetName = "未命名分类";
+                }
+                sheetName = sheetName.replaceAll("[\\\\/:*?\\[\\]]", "_");
+                if (sheetName.length() > 31) {
+                    sheetName = sheetName.substring(0, 31);
+                }
+                String baseSheetName = sheetName;
+                int suffix = 2;
+                while (usedSheetNames.contains(sheetName)) {
+                    String suffixStr = "(" + suffix + ")";
+                    int maxBaseLen = 31 - suffixStr.length();
+                    sheetName = (baseSheetName.length() > maxBaseLen ? baseSheetName.substring(0, maxBaseLen) : baseSheetName) + suffixStr;
+                    suffix++;
+                }
+                usedSheetNames.add(sheetName);
+
+                excel.addSheet(sheetName, rootNode.getTreeName(), exportHeaders, exportData);
+            }
+
+            String fileName = "知识库数据导出_" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
+            excel.write(response, fileName);
+            excel.dispose();
+
+        } catch (Exception e) {
+            throw new RuntimeException("导出失败:" + e.getMessage(), e);
+        }
+    }
+
     // ======================== 审核相关接口 ========================
 
     /**

+ 10 - 3
src/main/resources/mappings/modules/WorkKnowledgeBase/WorkKnowledgeBaseShareInfoDao.xml

@@ -99,8 +99,15 @@
         LEFT JOIN sys_user su ON su.id = a.create_by AND su.del_flag = '0'
         <where>
             a.del_flag = '0'
+            <!-- 按指定节点ID列表查询(当前节点+祖先节点) -->
+            <if test="treeNodeIds != null and treeNodeIds.size() > 0">
+                AND a.tree_node_id IN
+                <foreach collection="treeNodeIds" item="tnId" open="(" separator="," close=")">
+                    #{tnId}
+                </foreach>
+            </if>
             <!-- 匹配当前选中节点及其所有子节点的数据 -->
-            <if test="entity.treeNodeId != null and entity.treeNodeId != ''">
+            <if test="(treeNodeIds == null or treeNodeIds.size() == 0) and entity.treeNodeId != null and entity.treeNodeId != ''">
                 AND a.tree_node_id IN (
                     SELECT t.id FROM work_knowledge_base_tree_info t
                     WHERE (t.id = #{entity.treeNodeId}
@@ -110,8 +117,8 @@
                       AND t.del_flag = '0'
                 )
             </if>
-            <!-- 兆底:若无treeNodeId但有rootId,则查根节点下所有节点 -->
-            <if test="(entity.treeNodeId == null or entity.treeNodeId == '') and entity.rootId != null and entity.rootId != ''">
+            <!-- 兆底:若无treeNodeId和treeNodeIds但有rootId,则查根节点下所有节点 -->
+            <if test="(treeNodeIds == null or treeNodeIds.size() == 0) and (entity.treeNodeId == null or entity.treeNodeId == '') and entity.rootId != null and entity.rootId != ''">
                 AND a.tree_node_id IN (
                     SELECT t.id FROM work_knowledge_base_tree_info t
                     WHERE (t.id = #{entity.rootId} OR t.root_id = #{entity.rootId})

+ 19 - 0
src/main/resources/mappings/modules/WorkKnowledgeBase/WorkKnowledgeBaseTreeInfoDao.xml

@@ -169,4 +169,23 @@
         ORDER BY a.sort ASC
     </select>
 
+    <!-- 根据父节点ID查询直接子节点 -->
+    <select id="findDirectChildrenByParentId" resultType="com.jeeplus.modules.WorkKnowledgeBase.entity.WorkKnowledgeBaseTreeInfo">
+        SELECT <include refid="treeColumns"/>
+        FROM work_knowledge_base_tree_info a
+        WHERE a.parent_id = #{parentId}
+          AND a.del_flag = '0'
+        ORDER BY a.sort ASC
+    </select>
+
+    <!-- 根据根节点ID查询所有子孙节点(不含根节点本身) -->
+    <select id="findAllDescendantsByRootId" resultType="com.jeeplus.modules.WorkKnowledgeBase.entity.WorkKnowledgeBaseTreeInfo">
+        SELECT <include refid="treeColumns"/>
+        FROM work_knowledge_base_tree_info a
+        WHERE a.root_id = #{rootId}
+          AND a.parent_id != '0'
+          AND a.del_flag = '0'
+        ORDER BY a.parent_ids ASC, a.sort ASC
+    </select>
+
 </mapper>

+ 26 - 0
src/main/webapp/webpage/modules/WorkKnowledgeBase/workKnowledgeBaseShareList.jsp

@@ -466,6 +466,26 @@
             $('#searchForm').submit();
         }
 
+        /** 多页签导出(通过iframe提交,避免页面跳转) */
+        function exportAllTabs() {
+            var form = $('#searchForm');
+            var iframeName = 'exportIframe_' + new Date().getTime();
+            var iframe = $('<iframe>', {
+                name: iframeName,
+                style: 'display:none;',
+                id: iframeName
+            }).appendTo('body');
+            var cloneForm = form.clone();
+            cloneForm.attr('action', '${ctx}/workKnowledgeBase/share/exportAllTabs');
+            cloneForm.attr('target', iframeName);
+            cloneForm.css('display', 'none').appendTo('body');
+            cloneForm.submit();
+            setTimeout(function() {
+                cloneForm.remove();
+                iframe.remove();
+            }, 60000);
+        }
+
         /** 获取审核状态显示配置 */
         /*function getWorkKnowledgeAuditState(auditStatus) {
             var statusMap = {0:'草稿', 1:'未审核', 2:'审核中', 4:'审核未通过', 5:'审核通过'};
@@ -625,6 +645,12 @@
                                      导入
                                 </button>
                             </shiro:hasPermission>
+                            <shiro:hasPermission name="workKnowledgeBase:share:export">
+                                <button class="layui-btn layui-btn-sm layui-bg-yellow"
+                                        onclick="exportAllTabs()">
+                                    <i class="glyphicon glyphicon-export"></i> 导出
+                                </button>
+                            </shiro:hasPermission>
                         </c:if>
                         <button class="layui-btn layui-btn-sm" onclick="search()" title="刷新">刷新</button>
                     </div>