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

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

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

問(wèn)題背景

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

解決方案

技術(shù)選型

使用exceljs + file-saver組合:

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

功能特性

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

完整實(shí)現(xiàn)代碼

步驟1:安裝依賴(lài)

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 圖片下標(biāo)列 [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; // 默認(rèn)列寬
        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ù)導(dǎo)出_${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">導(dǎo)出Excel</button>
</template>

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

const headers = ['名稱(chēng)', '價(jià)格', '主圖'];
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>

擴(kuò)展配置

設(shè)置列寬

    headers.forEach((_, colIndex) => {
        let width = 20; // 默認(rèn)列寬
        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'
        };
    });

導(dǎo)出樣式

注意事項(xiàng)

  1. 圖片規(guī)范

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

    • 推薦數(shù)據(jù)量:<1000行
    • 單文件圖片:<50張
    • 大文件建議分頁(yè)導(dǎo)出
  3. 常見(jiàn)問(wèn)題

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

實(shí)現(xiàn)原理

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

  2. 圖片處理

    • 自動(dòng)剝離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)勢(shì):

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

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

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

相關(guān)文章

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

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

    這篇文章主要介紹了element-ui 遠(yuǎn)程搜索組件el-select在項(xiàng)目中組件化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • vue的指令和插值問(wèn)題匯總

    vue的指令和插值問(wèn)題匯總

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

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

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

    vue項(xiàng)目中路由跳轉(zhuǎn)頁(yè)面不變問(wèn)題及解決

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

    vue 解決addRoutes多次添加路由重復(fù)的操作

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

    Vue組件與生命周期詳細(xì)講解

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

    vue中播放rtsp流的方法實(shí)例詳解

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

    解決vue3傳屬性時(shí)報(bào)錯(cuò)[Vue?warn]:Component?is?missing?template?or

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

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

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

    Vue?Router組合布局用法詳解

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

最新評(píng)論

涞源县| 玛多县| 阜康市| 沈丘县| 宁明县| 杭州市| 英德市| 通渭县| 葫芦岛市| 竹溪县| 台南市| 博湖县| 辽源市| 兴城市| 河西区| 噶尔县| 耿马| 四川省| 招远市| 敦化市| 兴城市| 通海县| 久治县| 巴东县| 育儿| 林芝县| 新密市| 宜城市| 巍山| 陆丰市| 茂名市| 临泉县| 织金县| 饶河县| 双流县| 桦川县| 永川市| 维西| 安义县| 息烽县| 图木舒克市|