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

Vue3中使用exceljs和file-saver實現(xiàn)Excel導出(含圖片導出)功能完整方案

 更新時間:2025年07月22日 09:29:43   作者:叫我OldFe  
ExcelJS是一個用于在Node.js和瀏覽器中創(chuàng)建、讀取和修改Excel文件的強大JavaScript庫,這篇文章主要介紹了Vue3中使用exceljs和file-saver實現(xiàn)Excel導出(含圖片導出)功能的相關(guān)資料,需要的朋友可以參考下

問題背景

在Vue3項目中,當我們需要將包含圖片的數(shù)據(jù)導出到Excel時,常用的sheetjs/xlsx庫存在局限性:無法直接導出圖片到單元格。本文將提供完整的解決方案,封裝可直接復用的工具函數(shù)。

解決方案

技術(shù)選型

使用exceljs + file-saver組合:

  • exceljs:支持圖片插入的Excel操作庫
  • file-saver:前端文件保存工具

功能特性

? 支持多圖片列導出
? 自動識別Base64和DataURL格式
? 精確控制圖片位置
? 異常處理與性能優(yōu)化

完整實現(xiàn)代碼

步驟1:安裝依賴

npm install exceljs file-saver

步驟2:創(chuàng)建工具文件

// utils/excelExport.js
import ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';

/**
 *
 * @param headers 表頭,[表頭1,表頭2,...]          必填
 * @param tableData 內(nèi)容 [data1,data2,...]       必填
 * @param imageIndex 圖片下標列 [0, 1, 2 , ...],     非必填
 * @returns {Promise<void>}
 */
export const exportToExcel = async (headers, tableData, imageIndex = []) => {
    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Sheet1');

    // 添加表頭
    const headerRow = worksheet.addRow(headers);

    // 設(shè)置表頭樣式
    headerRow.eachCell((cell) => {
        cell.font = {
            bold: true,
            color: { argb: 'FFFFFFFF' },
            size: 12
        };
        cell.fill = {
            type: 'pattern',
            pattern: 'solid',
            fgColor: { argb: 'FF007BFF' }
        };
        cell.alignment = {
            horizontal: 'center',
            vertical: 'middle'
        };
    });

    // 設(shè)置列寬
    headers.forEach((_, colIndex) => {
        let width = 20; // 默認列寬
        if (imageIndex.includes(colIndex)) {
            width = 30; // 圖片列更寬
        }
        worksheet.getColumn(colIndex + 1).width = width;
    });

    // 處理數(shù)據(jù)行(修改為異步)
    for (let rowIdx = 0; rowIdx < tableData.length; rowIdx++) {
        const row = tableData[rowIdx];
        const dataRow = worksheet.addRow(row);

        if (imageIndex.length > 0) {
            // 使用Promise.all處理多列圖片
            await Promise.all(imageIndex.map(async (colIdx) => {
                const imgUrl = row[colIdx];
                if (!imgUrl) return;

                try {
                    const parsed = await parseImageData(imgUrl);
                    if (!parsed) return;

                    const imageId = workbook.addImage({
                        base64: parsed.base64,
                        extension: parsed.extension,
                    });

                    const colLetter = getExcelColumnLetter(colIdx);
                    const excelRow = rowIdx + 2;

                    worksheet.addImage(imageId, {
                        tl: { col: colIdx, row: excelRow - 1 },
                        br: { col: colIdx + 1, row: excelRow },
                        editAs: 'oneCell' // 更穩(wěn)定的定位方式
                    });
                } catch (error) {
                    console.error(`圖片處理失?。ㄐ?{rowIdx + 1}列${colIdx + 1}):`, error);
                }
            }));
        }
    }

    // 生成文件
    const buffer = await workbook.xlsx.writeBuffer();
    saveAs(new Blob([buffer]), `數(shù)據(jù)導出_${new Date().getTime()}.xlsx`);
};

// 列索引轉(zhuǎn)Excel字母(如:0 -> A, 1 -> B)
const getExcelColumnLetter = (column) => {
    let letter = '';
    let col = column + 1;
    while (col > 0) {
        const remainder = (col - 1) % 26;
        letter = String.fromCharCode(65 + remainder) + letter;
        col = Math.floor((col - 1) / 26);
    }
    return letter;
};

// 新增:將圖片URL轉(zhuǎn)換為base64
const urlToBase64 = async (url) => {
    try {
        const response = await fetch(url);
        const blob = await response.blob();
        return new Promise((resolve) => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(reader.result);
            reader.readAsDataURL(blob);
        });
    } catch (error) {
        console.error('圖片轉(zhuǎn)換失敗:', error);
        return null;
    }
};

// 修改后的解析函數(shù)
const parseImageData = async (imgData) => {
    if (!imgData) return null;

    // 處理URL情況
    if (imgData.startsWith('http')) {
        const dataURL = await urlToBase64(imgData);
        if (!dataURL) return null;

        const [header, base64] = dataURL.split(';base64,');
        const extension = header.split('/')[1];
        return { base64, extension };
    }

    // 原有處理dataURL的邏輯
    if (imgData.startsWith('data:image')) {
        const [header, base64] = imgData.split(';base64,');
        if (!header || !base64) return null;
        const extension = header.split('/')[1];
        return { base64, extension };
    }

    return null;
};

使用示例

Vue3組件調(diào)用

<template>
  <button @click="handleExport">導出Excel</button>
</template>

<script setup>
import { exportDataToExcel } from '@/utils/excelExport';

const headers = ['名稱', '價格', '主圖'];
const tableData = [
  ['商品A', 99.9, 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'], 
  ['商品B', 199.9, '']
];
const imageIndex = [2]; // 第三列為圖片列

const handleExport = async () => {
  await exportDataToExcel(headers, tableData, imageIndex);
};
</script>

擴展配置

設(shè)置列寬

    headers.forEach((_, colIndex) => {
        let width = 20; // 默認列寬
        if (imageIndex.includes(colIndex)) {
            width = 30; // 圖片列更寬
        }
        worksheet.getColumn(colIndex + 1).width = width;
    });

設(shè)置表頭樣式

headerRow.eachCell((cell) => {
        cell.font = {
            bold: true,
            color: { argb: 'FFFFFFFF' },
            size: 12
        };
        cell.fill = {
            type: 'pattern',
            pattern: 'solid',
            fgColor: { argb: 'FF007BFF' }
        };
        cell.alignment = {
            horizontal: 'center',
            vertical: 'middle'
        };
    });

導出樣式

注意事項

  1. 圖片規(guī)范

    • 建議使用PNG格式
    • 單圖片尺寸不超過500x500px
    • Base64字符串需完整
  2. 性能建議

    • 推薦數(shù)據(jù)量:<1000行
    • 單文件圖片:<50張
    • 大文件建議分頁導出
  3. 常見問題

    • 圖片不顯示:檢查Base64格式是否正確
    • 位置偏移:調(diào)整tl/br定位參數(shù)
    • 內(nèi)存溢出:分批次處理數(shù)據(jù)

實現(xiàn)原理

  1. 坐標轉(zhuǎn)換
    通過getExcelColumnLetter函數(shù)將數(shù)字索引轉(zhuǎn)換為Excel字母坐標

  2. 圖片處理

    • 自動剝離DataURL頭信息
    • 支持純Base64直接使用
  3. 定位系統(tǒng)

    // tl: 左上角定位(top-left)
    // br: 右下角定位(bottom-right)
    { 
      tl: { col: 0, row: 0 }, // A1單元格
      br: { col: 1, row: 1 }  // B2單元格
    }
    

總結(jié)

本文提供的方案具有以下優(yōu)勢:

  1. 開箱即用:封裝完整工具函數(shù)
  2. 靈活配置:支持多圖片列、自定義樣式
  3. 健壯性強:完善的異常處理機制

建議根據(jù)實際業(yè)務(wù)需求調(diào)整圖片定位參數(shù)和樣式配置,對于復雜報表建議結(jié)合后端生成方案。

到此這篇關(guān)于Vue3中使用exceljs和file-saver實現(xiàn)Excel導出(含圖片導出)功能的文章就介紹到這了,更多相關(guān)Vue3導出Excel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • element-ui 遠程搜索組件el-select在項目中組件化的實現(xiàn)代碼

    element-ui 遠程搜索組件el-select在項目中組件化的實現(xiàn)代碼

    這篇文章主要介紹了element-ui 遠程搜索組件el-select在項目中組件化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-12-12
  • vue的指令和插值問題匯總

    vue的指令和插值問題匯總

    Vue?是一套用于構(gòu)建用戶界面的漸進式框架,Vue?的核心庫只關(guān)注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合,這篇文章主要介紹了vue的指令和插值總結(jié),需要的朋友可以參考下
    2022-10-10
  • vue 雙向數(shù)據(jù)綁定的實現(xiàn)學習之監(jiān)聽器的實現(xiàn)方法

    vue 雙向數(shù)據(jù)綁定的實現(xiàn)學習之監(jiān)聽器的實現(xiàn)方法

    這篇文章主要介紹了vue 雙向數(shù)據(jù)綁定的實現(xiàn)學習之監(jiān)聽器的實現(xiàn)方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-11-11
  • vue項目中路由跳轉(zhuǎn)頁面不變問題及解決

    vue項目中路由跳轉(zhuǎn)頁面不變問題及解決

    這篇文章主要介紹了vue項目中路由跳轉(zhuǎn)頁面不變問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue 解決addRoutes多次添加路由重復的操作

    vue 解決addRoutes多次添加路由重復的操作

    這篇文章主要介紹了vue 解決addRoutes多次添加路由重復的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Vue組件與生命周期詳細講解

    Vue組件與生命周期詳細講解

    Vue的生命周期就是vue實例從創(chuàng)建到銷毀的全過程,也就是new Vue() 開始就是vue生命周期的開始。Vue 實例有?個完整的?命周期,也就是從開始創(chuàng)建、初始化數(shù)據(jù)、編譯模版、掛載Dom -> 渲染、更新 -> 渲染、卸載 等?系列過程,稱這是Vue的?命周期
    2022-10-10
  • vue中播放rtsp流的方法實例詳解

    vue中播放rtsp流的方法實例詳解

    最近有個需求是前端在瀏覽器顯示攝像頭傳回的RTSP視頻流,下面這篇文章主要給大家介紹了關(guān)于vue中播放rtsp流的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • 解決vue3傳屬性時報錯[Vue?warn]:Component?is?missing?template?or?render?function

    解決vue3傳屬性時報錯[Vue?warn]:Component?is?missing?template?or

    這篇文章主要給大家介紹了關(guān)于解決vue3傳屬性時報錯[Vue?warn]:Component?is?missing?template?or?render?function的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-01-01
  • Vue+elementUI實現(xiàn)動態(tài)展示列表的數(shù)據(jù)

    Vue+elementUI實現(xiàn)動態(tài)展示列表的數(shù)據(jù)

    這篇文章主要介紹了Vue+elementUI實現(xiàn)動態(tài)展示列表的數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Vue?Router組合布局用法詳解

    Vue?Router組合布局用法詳解

    今天我們用一種新的布局方式,使用路由視圖來實現(xiàn)布局樣式,本文將給大家介紹如何使用Vue?Router組合布局,文中有詳細的代碼示例供大家參考,感興趣的同學可以跟著小編一起學習
    2023-05-05

最新評論

乌审旗| 和田市| 景洪市| 吉木萨尔县| 泰宁县| 英德市| 广德县| 宽甸| 登封市| 商都县| 内江市| 岑巩县| 乐清市| 嘉荫县| 漠河县| 肇州县| 宣化县| 卢龙县| 铁岭市| 仪陇县| 徐水县| 同心县| 无为县| 偃师市| 郴州市| 阜南县| 海淀区| 汝南县| 梓潼县| 胶南市| 横峰县| 汨罗市| 钟山县| 郴州市| 微博| 福鼎市| 临清市| 江津市| 方山县| 疏勒县| 门头沟区|