首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> 關於JSP

JSP 多層表頭 Table 匯出 Excel 完整實作:TableToExcel.js 元件化方案

     編輯:關於JSP

將包含合併儲存格 (`rowspan`/`colspan`)、多層表頭的 HTML `

` 完整匯出為 Excel,是報表系統常見需求。本文提供成熟的 `TableToExcel.js` 元件,透過 ActiveXObject 自動化 Excel,精確還原合併儲存格、樣式、欄寬、自動換行等細節。

架構設計

  • TableToExcel 類別:封裝匯出邏輯,支援鏈式設定字體、邊框、顏色、對齊、自適應欄寬等選項。
  • saveAsExcel(tableID):便捷入口函式,一鍵匯出。
  • copyhtmltoexcel.js (備選):利用 `execCommand("Copy")` + `Worksheet.Paste()` 簡易方案,適合不需精確合併儲存格的場景。

核心程式碼:TableToExcel.js (完整版)

/**
 * HTML Table → Excel 自動化匯出元件
 * 相容 IE / Edge (IE 模式) ,需啟用 ActiveX
 * @param {string} tableID - 目標 table 元素 id
 */
function TableToExcel(tableID) {
    this.tableID = tableID;
    // 預設樣式
    this.tableBorder = 2;      // 邊框粗細 (1-4)
    this.backGround = 0;       // 背景色索引 (Excel 調色盤)
    this.fontColor = 1;        // 字體色索引
    this.fontSize = 10;        // 字體大小
    this.fontStyle = "微軟正黑體"; // 字體名稱
    this.rowHeight = -1;       // 行高 (-1 自動)
    this.columnWidth = 7;      // 預設欄寬
    this.lineWrap = true;      // 自動換行
    this.textAlign = -4108;    // 對齊:-4108=置中, -4131=左, -4152=右
    this.autoFit = true;       // 自動調整欄寬
}

// 設定方法 (支援鏈式呼叫)
TableToExcel.prototype.setTableBorder = function(v) { this.tableBorder = v; return this; };
TableToExcel.prototype.setBackGround = function(v) { this.backGround = v; return this; };
TableToExcel.prototype.setFontColor = function(v) { this.fontColor = v; return this; };
TableToExcel.prototype.setFontSize = function(v) { this.fontSize = v; return this; };
TableToExcel.prototype.setFontStyle = function(v) { this.fontStyle = v; return this; };
TableToExcel.prototype.setRowHeight = function(v) { this.rowHeight = v; return this; };
TableToExcel.prototype.setColumnWidth = function(v) { this.columnWidth = v; return this; };
TableToExcel.prototype.isLineWrap = function(v) { this.lineWrap = v; return this; };
TableToExcel.prototype.setTextAlign = function(v) { this.textAlign = v; return this; };
TableToExcel.prototype.isAutoFit = function(v) { this.autoFit = v; return this; };

/** 核心匯出邏輯 */
TableToExcel.prototype.getExcelFile = function() {
    var excel, workbook, sheet, htmlTable, totalRow, totalCol, cellMap;
    try {
        excel = new ActiveXObject("Excel.Application");
    } catch (e) {
        alert("無法啟動 Excel!請確認已安裝 Excel 並調整 IE 安全性設定:\n" +
              "工具 → 網際網路選項 → 安全性 → 自訂層級 → ActiveX 控制項與增益集 → " +
              "「對未標記為安全的 ActiveX 控制項初始化並指令碼執行」選「啟用」");
        return false;
    }

    excel.Visible = true;
    workbook = excel.Workbooks.Add();
    excel.DisplayAlerts = false;
    // 刪除多餘工作表 (預設 3 張)
    workbook.Worksheets(3).Delete();
    workbook.Worksheets(2).Delete();
    excel.DisplayAlerts = true;
    sheet = workbook.ActiveSheet;

    // 1. 掃描目標 table,計算實際行數、列數 (含合併)
    htmlTable = document.getElementById(this.tableID);
    if (!htmlTable) { alert("找不到 table: " + this.tableID); return; }

    totalRow = htmlTable.rows.length;
    totalCol = 0;
    for (var c = 0; c < htmlTable.rows[0].cells.length; c++) {
        totalCol += htmlTable.rows[0].cells[c].colSpan || 1;
    }

    // 2. 建立二維陣列標記儲存格佔用狀態 (避開合併區域)
    cellMap = [];
    for (var r = 0; r <= totalRow; r++) {
        cellMap[r] = [];
        for (var c = 0; c <= totalCol; c++) cellMap[r][c] = false;
    }

    // 3. 設定全域預設欄寬/行高
    if (this.columnWidth != -1) sheet.Columns.ColumnWidth = this.columnWidth;
    if (this.rowHeight != -1) sheet.Rows.RowHeight = this.rowHeight;

    // 4. 逐儲存格轉換
    for (var r = 0; r < htmlTable.rows.length; r++) {
        var excelRow = r + 1; // Excel 1-based
        var excelCol = 1;
        var htmlRow = htmlTable.rows[r];

        for (var c = 0; c < htmlRow.cells.length; c++) {
            var htmlCell = htmlRow.cells[c];
            var colSpan = htmlCell.colSpan || 1;
            var rowSpan = htmlCell.rowSpan || 1;

            // 尋找下一個可用欄位
            while (excelCol <= totalCol && cellMap[excelRow][excelCol]) excelCol++;
            if (excelCol > totalCol) break;

            var startCell = sheet.Cells(excelRow, excelCol);
            var endCell = sheet.Cells(excelRow + rowSpan - 1, excelCol + colSpan - 1);
            var range = sheet.Range(startCell, endCell);

            // 合併儲存格 (若跨列/跨行)
            if (rowSpan > 1 || colSpan > 1) {
                range.Merge();
            }

            // 樣式套用
            range.HorizontalAlignment = this.textAlign;
            range.Font.Size = this.fontSize;
            range.Font.Name = this.fontStyle;
            range.WrapText = this.lineWrap;
            range.Interior.ColorIndex = this.backGround;
            range.Font.ColorIndex = this.fontColor;

            if (this.tableBorder != -1) {
                var borders = [1, 2, 3, 4]; // xlEdgeTop, xlEdgeBottom, xlEdgeLeft, xlEdgeRight
                for (var b = 0; b < borders.length; b++) {
                    range.Borders(borders[b]).Weight = this.tableBorder;
                }
            }

            // 寫入內容 (innerText 純文字,避免 HTML 標籤污染)
            range.Value = htmlCell.innerText;

            // 標記佔用
            for (var rr = excelRow; rr < excelRow + rowSpan; rr++) {
                for (var cc = excelCol; cc < excelCol + colSpan; cc++) {
                    cellMap[rr][cc] = true;
                }
            }

            excelCol += colSpan;
        }
    }

    // 5. 自動調整欄寬
    if (this.autoFit) sheet.Columns.AutoFit();

    // 6. 釋放 COM 物件
    excel.UserControl = true; // 讓使用者控制 Excel 生命週期
    excel = null; workbook = null; sheet = null;
};

便捷入口函式

function saveAsExcel(tableID) {
    var tb = new TableToExcel(tableID);
    tb.setFontStyle("微軟正黑體")
      .setFontSize(10)
      .setTableBorder(2)
      .setColumnWidth(7)
      .isLineWrap(false)
      .isAutoFit(true)
      .getExcelFile();
}

JSP 頁面整合範例

<%@ page contentType="text/html;charset=UTF-8" %>
<html><head>
    <script src="TableToExcel.js"></script>
</head><body>

<h3>多層表頭報表示例</h3>
<table id="reportTable" border="1" cellspacing="0" cellpadding="4">
    <tr style="background:#4472C4; color:white;">
        <th rowspan="2">年度</th>
        <th colspan="4">季度營收 (萬元)</th>
        <th rowspan="2">年增率</th>
    </tr>
    <tr style="background:#5B9BD5; color:white;">
        <th>Q1</th><th>Q2</th><th>Q3</th><th>Q4</th>
    </tr>
    <tr><td>2023</td><td>1200</td><td>1350</td><td>1400</td><td>1550</td><td>12.5%</td></tr>
    <tr><td>2024</td><td>1300</td><td>1420</td><td>1480</td><td>1620</td><td>8.3%</td></tr>
</table>
<br/>
<input type="button" value="匯出 Excel" onclick="saveAsExcel('reportTable')" />

</body></html>

備選方案:copyhtmltoexcel.js (簡易版)

原理:`document.body.createTextRange().moveToElementText(el).execCommand("Copy")` → `sheet.Paste()`。
優點:程式碼極簡、自動保留 HTML 表格結構。
缺點:無法精確控制合併儲存格邊框、樣式;大量資料效能差;同樣需 ActiveX。

部署與安全性設定 (必要步驟)

  1. 用戶端必須安裝 Excel** (不可只裝 Viewer)。
  2. IE 設定:
    「工具 → 網際網路選項 → 安全性 → 信任的網站/區域 → 自訂層級」
    → 「ActiveX 控制項與增益集」
    → 「對未標記為安全的 ActiveX 控制項初始化並指令碼執行」 → **啟用**
    → 「執行 ActiveX 控制項」 → **啟用**
  3. 將網站加入「信任的網站」區域,降低安全性提示。
  4. 現代瀏覽器 (Edge/Chrome/Firefox) 不支援 ActiveX**

現代化替代方案 (無 ActiveX、跨瀏覽器)

方案原理優點缺點
SheetJS (xlsx.js)純 JS 解析 Table → 生成 .xlsx 二進位 Blob → `saveAs()` 下載無外掛、跨平台、支援樣式/合併儲存格、效能極佳需引入庫、大檔案記憶體壓力
ExcelJS功能更豐富的 JS Excel 操作庫支援串流寫入、公式、圖表、樣式完整體積較大
後端生成 (Apache POI / NPOI / ClosedXML)Server 端生成 .xlsx → 提供下載連結無瀏覽器限制、邏輯集中、安全性高、支援超大檔需往返 Server、佔用後端資源

SheetJS 快速上手 (前端純 JS)

<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<script>
function exportTableToExcel(tableId, filename) {
    var table = document.getElementById(tableId);
    var wb = XLSX.utils.table_to_book(table, { sheet: "Report", raw: true });
    // 可進一步調整欄寬、樣式...
    XLSX.writeFile(wb, filename || "Report.xlsx");
}
</script>
<button onclick="exportTableToExcel('reportTable', '年度營收報表.xlsx')">匯出 (SheetJS)</button>
`TableToExcel.js` 為傳統 IE+ActiveX 環境提供**像素級精確控制**的 Table→Excel 匯出方案,完美處理多層表頭、合併儲存格、樣式。現代專案**強烈建議改用 SheetJS/xlsx.js** 或**後端生成**,擺脫 ActiveX 依賴,實現跨瀏覽器、跨平台、無安全性設定困擾的匯出體驗。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved