最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用xlsx-js-style操作Excel文件樣式全過程

 更新時(shí)間:2026年02月04日 10:50:43   作者:王闊闊  
xlsx-js-style庫在使用純JavaScript進(jìn)行Excel導(dǎo)出方面提供了強(qiáng)大的功能,不僅處理了合并單元格的顯示問題,還支持動(dòng)態(tài)表頭的插入,這篇文章主要介紹了使用xlsx-js-style操作Excel文件樣式的相關(guān)資料,需要的朋友可以參考下

excel文件內(nèi)容效果圖

xlsx-js-style 插件

插件描述

xlsx-js-style 是一個(gè)用于增強(qiáng) SheetJS (也稱 xlsx) 庫功能的開源 JavaScript 庫,它允許開發(fā)者在使用 SheetJS 生成或操作 Excel 文件(.xlsx)時(shí),添加單元格樣式,而原生的 SheetJS 庫對樣式的支持非常有限。

主要功能(樣式支持)

通過 xlsx-js-style,你可以為 Excel 單元格設(shè)置以下樣式:

  • 字體樣式:字體名稱、大小、顏色、加粗、斜體、下劃線等
  • 對齊方式:水平對齊(左、中、右)、垂直對齊(上、中、下)、自動(dòng)換行
  • 邊框:上下左右邊框的樣式和顏色
  • 填充(背景色):純色填充、漸變填充
  • 數(shù)字格式:日期、貨幣、百分比等格式化
  • 合并單元格:支持帶樣式的合并

vue中使用xlsx-js-style

安裝

npm install xlsx-js-style

基于xlsx-js-style插件封裝的工具函數(shù)

// @/utils/exportExcel.js
import XLSX from "xlsx-js-style";

/**
 * 使用方式:
 *      Excel.export(columns, dataSource, "文件名")  // 前兩個(gè)入?yún)?el-table 組件的同名屬性值
 *      支持通過 columns 中的 show 屬性控制是否導(dǎo)出該字段
 *      當(dāng) show: false 時(shí),該字段將不會(huì)被導(dǎo)出到 Excel 中
 *
 * 使用示例:
 *      const columns = [
 *        { title: '姓名', dataIndex: 'name' },
 *        { title: '年齡', dataIndex: 'age', show: false },  // 該字段不會(huì)被導(dǎo)出
 *        { title: '性別', dataIndex: 'sex' }
 *      ]
 *
 * 注意:
 *      有的版本庫可能支持顏色名(如 red),但為了確保兼容性和穩(wěn)定性,建議使用十六進(jìn)制顏色代碼(如 #FF0000)
 */
const Excel = {
  /**
   * @param columns       使用  el-table 組件時(shí)的 columns 數(shù)據(jù) 格式:[{ title: '地區(qū)', dataIndex: 'districtName' },{ title: '名稱' ,children[{ title: '年齡', dataIndex: 'age' }, { title: '性別', dataIndex: 'sex' }]
   * @param dataSource    使用  el-table 組件時(shí)的 data-source 數(shù)據(jù)
   * @param fileName      excel導(dǎo)出時(shí)的文件名
   */
  export(columns, dataSource, fileName) {
    console.log("Excel.export 調(diào)用參數(shù):", { columns, dataSource, fileName });

    const columnHeight = this.columnHeight(columns);
    const columnWidth = this.columnWidth(columns);
    console.log("列高度和寬度:", { columnHeight, columnWidth });

    const header = [];
    for (let rowNum = 0; rowNum < columnHeight; rowNum++) {
      header[rowNum] = [];
      for (let colNum = 0; colNum < columnWidth; colNum++) {
        header[rowNum][colNum] = "";
      }
    }
    let offset = 0;
    const mergeRecord = [];
    for (const item of columns) {
      this.generateExcelColumn(header, 0, offset, item, mergeRecord);
      offset += this.treeWidth(item);
    }

    console.log("生成的表頭:", header);

    const dataArray = this.jsonDataToArray(columns, dataSource);
    console.log("轉(zhuǎn)換后的數(shù)據(jù)數(shù)組:", dataArray);

    header.push(...dataArray);

    console.log("最終的數(shù)據(jù)結(jié)構(gòu):", header);

    const ws = this.aoa_to_sheet(header, columnHeight);
    ws["!merges"] = mergeRecord;
    // 頭部凍結(jié)
    ws["!freeze"] = {
      xSplit: "1",
      ySplit: "" + columnHeight,
      topLeftCell: "B" + (columnHeight + 1),
      activePane: "bottomRight",
      state: "frozen",
    };
    // 列寬
    ws["!cols"] = [{ wpx: 165 }, { wpx: 165 }]; //設(shè)定前兩列列寬
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "sheet1");
    XLSX.writeFile(wb, fileName + ".xlsx");
  },
  aoa_to_sheet(data, headerRows) {
    const ws = {};
    const range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
    // 遍歷步驟1里面的二維數(shù)組數(shù)據(jù)
    for (let R = 0; R !== data.length; ++R) {
      for (let C = 0; C !== data[R].length; ++C) {
        if (range.s.r > R) {
          range.s.r = R;
        }
        if (range.s.c > C) {
          range.s.c = C;
        }
        if (range.e.r < R) {
          range.e.r = R;
        }
        if (range.e.c < C) {
          range.e.c = C;
        }
        // / 構(gòu)造cell對象,對所有excel單元格使用如下樣式
        let cell;
        if (
          typeof data[R][C] === "object" &&
          data[R][C] !== null &&
          data[R][C].v !== undefined
        ) {
          // 此處預(yù)留了自定義設(shè)置樣式的功能,通過重寫recursiveChildrenData方法,可為每一個(gè)單元格傳入樣式屬性
          cell = data[R][C];
        } else {
          // 處理 null、undefined 等值,轉(zhuǎn)換為空字符串,但保持?jǐn)?shù)據(jù)傳遞
          const cellValue =
            data[R][C] === null || data[R][C] === undefined ? "" : data[R][C];
          cell = {
            v: cellValue,
            s: {
              font: { name: "宋體", sz: 11, color: { auto: 1 } },
              // 單元格對齊方式
              alignment: {
                // / 自動(dòng)換行
                wrapText: 1,
                // 水平居中
                horizontal: "center",
                // 垂直居中
                vertical: "center",
              },
            },
          };
        }
        // 頭部列表加邊框
        if (R < headerRows) {
          cell.s.border = {
            top: { style: "thin", color: { rgb: "000000" } },
            left: { style: "thin", color: { rgb: "000000" } },
            bottom: { style: "thin", color: { rgb: "000000" } },
            right: { style: "thin", color: { rgb: "000000" } },
          };
          // 背景色
          cell.s.fill = {
            patternType: "solid",
            fgColor: { rgb: "DDD9C4" },
            bgColor: { rgb: "8064A2" },
          };
        }
        // 合計(jì)行加邊框和背景色
        if (R === data.length - 1 && data[R][0] === "合計(jì)") {
          cell.s.border = {
            top: { style: "thin", color: { rgb: "000000" } },
            left: { style: "thin", color: { rgb: "000000" } },
            bottom: { style: "thin", color: { rgb: "000000" } },
            right: { style: "thin", color: { rgb: "000000" } },
          };
          // 給合計(jì)行一個(gè)不同的背景色
          cell.s.fill = {
            patternType: "solid",
            fgColor: { theme: 2, tint: 0.3999755851924192, rgb: "C6EFCE" },
            bgColor: { theme: 2, tint: 0.3999755851924192, rgb: "C6EFCE" },
          };
          // 合計(jì)行字體加粗
          cell.s.font = {
            name: "宋體",
            sz: 11,
            color: { auto: 1 },
            bold: true,
          };
        }
        const cell_ref = XLSX.utils.encode_cell({ c: C, r: R });
        // 該單元格的數(shù)據(jù)類型,只判斷了數(shù)值類型、布爾類型,字符串類型,省略了其他類型

        // 自己可以翻文檔加其他類型
        if (typeof cell.v === "number") {
          cell.t = "n";
        } else if (typeof cell.v === "boolean") {
          cell.t = "b";
        } else {
          cell.t = "s";
        }
        ws[cell_ref] = cell;
      }
    }
    if (range.s.c < 10000000) {
      ws["!ref"] = XLSX.utils.encode_range(range);
    }
    return ws;
  },
  generateExcelColumn(
    columnTable,
    rowOffset,
    colOffset,
    columnDefine,
    mergeRecord
  ) {
    // 如果設(shè)置了 show: false,則不生成該列
    if (columnDefine.show === false) {
      return;
    }
    const columnWidth = this.treeWidth(columnDefine);
    columnTable[rowOffset][colOffset] = columnDefine.title;
    if (columnDefine.children) {
      mergeRecord.push({
        s: { r: rowOffset, c: colOffset },
        e: { r: rowOffset, c: colOffset + columnWidth - 1 },
      });
      let tempOffSet = colOffset;
      for (const child of columnDefine.children) {
        this.generateExcelColumn(
          columnTable,
          rowOffset + 1,
          tempOffSet,
          child,
          mergeRecord
        );
        tempOffSet += this.treeWidth(child);
      }
    } else {
      if (rowOffset !== columnTable.length - 1) {
        mergeRecord.push({
          s: { r: rowOffset, c: colOffset },
          e: { r: columnTable.length - 1, c: colOffset },
        });
      }
    }
  },
  columnHeight(column) {
    let height = 0;
    for (const item of column) {
      height = Math.max(this.treeHeight(item), height);
    }
    return height;
  },
  columnWidth(column) {
    let width = 0;
    for (const item of column) {
      width += this.treeWidth(item);
    }
    return width;
  },
  treeHeight(root) {
    if (root) {
      if (root.children && root.children.length !== 0) {
        let maxChildrenLen = 0;
        for (const child of root.children) {
          maxChildrenLen = Math.max(maxChildrenLen, this.treeHeight(child));
        }
        return 1 + maxChildrenLen;
      } else {
        return 1;
      }
    } else {
      return 0;
    }
  },
  treeWidth(root) {
    if (!root) return 0;
    // 如果設(shè)置了 show: false,則不計(jì)算該列寬度
    if (root.show === false) return 0;
    if (!root.children || root.children.length === 0) return 1;
    let width = 0;
    for (const child of root.children) {
      width += this.treeWidth(child);
    }
    return width;
  },
  jsonDataToArray(column, data) {
    const dataIndexes = [];
    for (const item of column) {
      dataIndexes.push(...this.getLeafDataIndexes(item));
    }
    return this.recursiveChildrenData(dataIndexes, data);
  },
  recursiveChildrenData(columnIndex, data) {
    const result = [];
    for (const rowData of data) {
      const row = [];
      for (const index of columnIndex) {
        // 確保 null 和 undefined 值被轉(zhuǎn)換為空字符串
        const value =
          rowData[index] === null || rowData[index] === undefined
            ? ""
            : rowData[index];
        row.push(value);
      }
      result.push(row);
      if (rowData.children) {
        result.push(
          ...this.recursiveChildrenData(columnIndex, rowData.children)
        );
      }
    }
    return result;
  },
  getLeafDataIndexes(root) {
    const result = [];
    if (root.children) {
      for (const child of root.children) {
        result.push(...this.getLeafDataIndexes(child));
      }
    } else {
      // 如果設(shè)置了 show: false,則不導(dǎo)出該字段
      if (root.show !== false) {
        result.push(root.dataIndex);
      }
    }
    return result;
  },
};
export default Excel;

組件中使用工具函數(shù)實(shí)現(xiàn)導(dǎo)出excel功能

// 關(guān)鍵代碼
<template>
	<el-button
    	type="warning"
        plain
        icon="el-icon-download"
        size="mini"
        @click="handleExport">導(dǎo)出</el-button>

	 <!-- 列表 -->
	<el-table
        v-loading="loading"
        :data="tableList"
        show-summary
        :summary-method="getSummaries"
      >
        <el-table-column label="序號" align="center" prop="seq" />
        <el-table-column label="列1" align="center" prop="administrativeArea" />
        <el-table-column label="列2" align="center" prop="totalHouseholds" />
        <el-table-column label="列3" align="center" prop="gasReplacementCoal" />
        <el-table-column
          label="列4"
          align="center"
          prop="electricityReplacementCoal"
        />
        <el-table-column label="列5" align="center">
          <el-table-column
            label="列5-1"
            align="center"
            prop="decentralizedTownshipCount"
          />
          <el-table-column
            label="列5-2"
            align="center"
            prop="decentralizedVillageCount"
          />
          <el-table-column
            label="列5-3"
            align="center"
            prop="decentralizedTotal"
          />
          <el-table-column
            label="列5-4"
            align="center"
            prop="decentralizedNaturalGas"
          />
          <el-table-column
            label="列5-5"
            align="center"
            prop="decentralizedElectricity"
          />
        </el-table-column>
        <el-table-column label="列6" align="center">
          <el-table-column
            label="列6-1"
            align="center"
            prop="committeeTownshipCount"
          />
          <el-table-column
            label="列6-2"
            align="center"
            prop="committeeVillageCount"
          />
          <el-table-column label="列6-3" align="center" prop="committeeTotal" />
          <el-table-column
            label="列6-4"
            align="center"
            prop="committeeNaturalGas"
          />
          <el-table-column
            label="列6-5"
            align="center"
            prop="committeeElectricity"
          />
        </el-table-column>
        <el-table-column label="列7" align="center" prop="type">
          <template slot-scope="scope">
            <dict-tag
              :options="dict.type.ledger_type"
              :value="scope.row.type"
            />
          </template>
        </el-table-column>
      </el-table>
</template>

<script>
import Excel from "@/utils/exportExcel.js"; // 引入工具函數(shù)
export default {
	name: "ExportExcel",
	dicts: ["ledger_type"],
	data() {
		return {
			tableList: [], //列表數(shù)據(jù) 
		}
	},
	computed: {
   		currentDate() {
      		const now = new Date();
      		const year = now.getFullYear();
      		const month = now.getMonth() + 1;
      		return `${year}年${month}月`;
    	},
  	},
	methods: {
		/** 導(dǎo)出按鈕操作 */
    	handleExport() {
      		// 定義導(dǎo)出的列結(jié)構(gòu)
     		 const columns = [
        		{
          			title: "列1",
          			dataIndex: "administrativeArea",
        		},
        		{
          			title: "列2",
          			dataIndex: "totalHouseholds",
        		},
        		{
          			title: "列3",
          			dataIndex: "gasReplacementCoal",
        		},
        		{
          			title: "列4",
          			dataIndex: "electricityReplacementCoal",
        		},
        		{
          			title: "列5",
          			children: [
            			{
              				title: "列5-1",
              				dataIndex: "decentralizedTownshipCount",
            			},
            			{
              				title: "列5-2",
              				dataIndex: "decentralizedVillageCount",
            			},
            			{
              				title: "列5-3",
              				dataIndex: "decentralizedTotal",
            			},
            			{
              				title: "列5-4",
              				dataIndex: "decentralizedNaturalGas",
            			},
            			{
              				title: "列5-5",
              				dataIndex: "decentralizedElectricity",
            			},
          			],
        		},
        		{
          			title: "列6",
          			children: [
            			{
              				title: "列6-1",
              				dataIndex: "committeeTownshipCount",
            			},
            			{
              				title: "列6-2",
              				dataIndex: "committeeVillageCount",
            			},
            			{
              				title: "列6-3",
              				dataIndex: "committeeTotal",
            			},
            			{
              				title: "列6-4",
              				dataIndex: "committeeNaturalGas",
            			},
            			{
              				title: "列6-5",
              				dataIndex: "committeeElectricity",
            			},
          			],
        		},
        		{
          			title: "列7",
          			dataIndex: "type",
        		},
      		];

      		// 生成合計(jì)行數(shù)據(jù)
      		const summaryData = this.generateSummaryData();
      		// 生成占比統(tǒng)計(jì)數(shù)據(jù)
      		const percentageData = this.generatePercentageData();
      		// 深拷貝table數(shù)據(jù)
      		const tableData = structuredClone(this.tableList);
      		// 將字典項(xiàng)轉(zhuǎn)換為對象,以 value 為鍵,label 為值
      		const typeMap = {};
      		this.dict.type.ledger_type.forEach((type) => {
        		typeMap[type.value] = type.label;
      		});
      		// 使用 map 方法對 tableData中匹配字典項(xiàng)數(shù)據(jù)進(jìn)行處理
      		const result = tableData.map((item) => {
        		return {
          			...item,
         			 type: typeMap[item.type] || "", // 如果沒有匹配到,type 賦值為空字符串
        		};
      		});
      		// 將合計(jì)行和占比統(tǒng)計(jì)添加到數(shù)據(jù)末尾
      		const exportData = [...result, summaryData, ...percentageData];
      		// 調(diào)用導(dǎo)出方法
      		Excel.export(columns, exportData, `測試_${this.currentDate}`);
    	},
    	// table合計(jì)行
    	getSummaries(param) {
      		const { columns, data } = param;
      		const sums = [];
      		// 指定需要合計(jì)的列的屬性
      		const propertiesToSum = [
        		"totalHouseholds",
        		"decentralizedTownshipCount",
        		"decentralizedVillageCount",
        		"decentralizedTotal",
        		"decentralizedNaturalGas",
        		"decentralizedElectricity",
        		"committeeTownshipCount",
        		"committeeVillageCount",
        		"committeeTotal",
        		"committeeNaturalGas",
        		"committeeElectricity",
      		];

      		columns.forEach((column, index) => {
        		if (index === 1) {
          			sums[index] = "合計(jì)";
          			return;
        		}
        		if (propertiesToSum.includes(column.property)) {
          			const values = data.map((item) => {
            			const value = item[column.property];
            			return typeof value === "number" ? Number(value) : undefined;
         			});
         	 		if (values.every((value) => typeof value === "number")) {
            			sums[index] = values.reduce((prev, curr) => prev + curr, 0);
          			} else {
            			sums[index] = "";
          			}
        		} else {
          			sums[index] = "";
        		}
      		});
      		return sums;
    	},
    	// 生成合計(jì)行數(shù)據(jù)
    	generateSummaryData() {
     		 const summaryData = {};
      		// 第一列顯示"合計(jì)"
      		summaryData.administrativeArea = "合計(jì)";
      		// 計(jì)算各列的合計(jì)值
      		const columns = [
        		"totalHouseholds",
        		"decentralizedTownshipCount",
        		"decentralizedVillageCount",
        		"decentralizedTotal",
        		"decentralizedNaturalGas",
        		"decentralizedElectricity",
        		"committeeTownshipCount",
        		"committeeVillageCount",
        		"committeeTotal",
        		"committeeNaturalGas",
        		"committeeElectricity",
      		];
      		
      		columns.forEach((column) => {
        		const sum = this.tableList.reduce((acc, item) => {
          			return acc + (Number(item[column]) || 0);
        		}, 0);
        		summaryData[column] = sum;
      		});
      		return summaryData;
    	},
   		// 生成占比統(tǒng)計(jì)數(shù)據(jù)
    	generatePercentageData() {
      		const percentageData = [];
      		// 添加空行
      		percentageData.push({
        		administrativeArea: "",
      		});
      		// 添加標(biāo)題行
      		percentageData.push({
        		administrativeArea: "主管部門負(fù)責(zé)工程占比統(tǒng)計(jì)",
      		});
      		// 添加各部門占比數(shù)據(jù)
      		percentageData.push({
        		administrativeArea: `部門1:0.01%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門2:0.02%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門3:0.03%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門4:0.04%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門5:0.05%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門6:0.06%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門7:0.07%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門8:0.08%`,
      		});
      		percentageData.push({
        		administrativeArea: `部門9:0.09%`,
      		});
      		return percentageData;
    	}
	},
}
</script>

注意

xlsx-js-style 并非官方維護(hù),是社區(qū)項(xiàng)目,可能在新版本 SheetJS 中出現(xiàn)兼容性問題。

替代方案

exceljs:功能更強(qiáng)大、維護(hù)更活躍的 Excel 操作庫,原生支持樣式、圖表、公式等,推薦用于新項(xiàng)目。

總結(jié)

到此這篇關(guān)于使用xlsx-js-style操作Excel文件樣式的文章就介紹到這了,更多相關(guān)xlsx-js-style操作Excel文件樣式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

珠海市| 麟游县| 乌兰浩特市| 隆德县| 临邑县| 泰宁县| 金门县| 武平县| 根河市| 广河县| 文登市| 赣州市| 辽宁省| 屯昌县| 广灵县| 昌邑市| 宁波市| 喜德县| 措勤县| 肇州县| 兖州市| 朝阳县| 绍兴市| 千阳县| 公主岭市| 温宿县| 成武县| 敦煌市| 阳谷县| 习水县| 碌曲县| 印江| 绩溪县| 广宁县| 郯城县| 方正县| 太原市| 望江县| 邯郸市| 荔浦县| 南充市|