將帶圖片的excel上傳後解析資料,然後將資料放入表格。主要是為了將資料傳給服務端
目前存在的問題
1、沒有判斷合併單元格的情況
2、圖片必須放在一個單元格中,超出則不顯示(也可以自己最佳化程式碼,去掉右下角座標的判斷)
需要安裝 xlsx , jszip
npm install xlsx jszip
<template> <div class="container"> <!-- 長傳元件 --> <el-upload action="" :before-upload="beforeUpload" :http-request="() => { }"> <el-button type="primary">匯入excel</el-button> </el-upload> <!-- 表格元件 --> <el-table :data="tableData" border style="width: auto; margin-top: 10px"> <el-table-column :prop="item.prop" :label="item.label" align="center" v-for="(item, index) in tableColumnLabel" :key="index"> <template #default="scope" v-if="item.prop == 'images'"> <img :src="img.path" alt="" style="width: 200px" v-for="img in scope.row.images" /> </template> </el-table-column> </el-table> </div> </template> <script setup lang="ts"> import { ref } from "vue"; import JSZip from "jszip"; // 引入jszip import type { JSZipObject } from 'jszip'; import * as XLSX from "xlsx"; // 引入xlsx const tableColumnLabel = ref(); // 獲取表頭內容 const tableData = ref<any[]>([]); // 表格資料 const tableImages = ref<imageList>([]); // 表格圖片 // 上傳excel const beforeUpload = async (file: any) => { // 處理解析圖片 tableImages.value = await getExcelImage(file); // 解析資料 getExcelData(file); } // 解析資料 const getExcelData = (file: Blob) => { let fileReader = new FileReader(); // 構建fileReader物件 fileReader.readAsArrayBuffer(file); // 讀取指定檔案內容 // 讀取操作完成時 fileReader.onload = function (e) { try { let data = e.target?.result; // 取得資料data let workbook = XLSX.read(data, { type: "binary" }); // 將data轉換成excel工作表資料 const worksheet = workbook.Sheets[workbook.SheetNames[0]]; // 獲取第一個工作表 /* * XLSX.utils.sheet_to_json 輸出JSON格式資料 * 獲取指定工作表中的資料sheetlist[],整個表中的資料存放在一個陣列sheetlist中; * sheetlist陣列中的每個元素均為一個陣列rowlist,是每一行的資料; * header 如果列太多,需要修改列的長度資料 可以使用預設值 1 */ const sheetlist = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); /** * 封裝資料 **********#######********* */ formatDate(sheetlist); } catch (e) { console.log("檔案型別不正確"); return; } }; } // 封裝資料 function formatDate(sheetlist: string | any[]) { const keys = ['images', 'title', 'value', 'type', 'images2']; // 處理資料 const setEmptyList = (ul: string | any[]) => { let obj = {} as { [x: string]: any }; for (let i = 0; i < ul.length; i++) { const item = ul[i]; obj[keys[i]] = item === undefined ? null : item } return obj } // 處理表頭 const setTableColumn = (list: any[]) => { return list.map((item: any, index: number) => { return { label: item, prop: keys[index] } }) } try { if (sheetlist.length < 1) return; tableColumnLabel.value = setTableColumn(sheetlist[0]); // 使用第一行作為獲取表頭 for (let i = 1; i < sheetlist.length; i++) { // 從第一行開始,因為第0行是表頭 console.log(sheetlist[i], 'item'); for (let j = 0; j < sheetlist[i].length; j++) { // 這裡的 i 就是行 j 就是列 // 計算圖片是否在這個單元格中 (這裡只判斷了一張,未判斷多張的情況,如果需要多張使用filter來計算) let filterList = tableImages.value.filter(item => { const { form, to } = item as { form: { x: string, y: string }, to: { x: string, y: string } }; const formX = Number(form.x); const formY = Number(form.y); const toX = Number(to.x); const toY = Number(to.y); // i行 , j列 return formX >= j && formY >= i && toX <= j && toY <= i; }); if (filterList.length > 0) { const imageItem = filterList.map(item => { return item }) sheetlist[i][j] = imageItem; } } let itemObj = setEmptyList(sheetlist[i]); tableData.value.push(itemObj); // 新增到el-table繫結的資料來源中 console.log(tableData); } console.log("tableData.value", tableData.value); } catch (error) { console.log(error); return; } } type imageList = { id: string, target: string, path: string, form?: { x: string, y: string, }, to?: { x: string, y: string } }[] // 解析圖片列表 const analysisImageList = async (zipLoadResultFiles: { [x: string]: JSZip.JSZipObject }) => { const imageIdKey = 'xl/drawings/_rels/drawing1.xml.rels';//圖片存放id檔案路徑 const fileContent = await zipLoadResultFiles[imageIdKey].async('string'); const parser = new DOMParser(); const xmldom = parser.parseFromString(fileContent, "text/xml"); const list = xmldom.getElementsByTagName("Relationship"); let results: imageList = []; for (var i = 0; i < list.length; i++) { const item = list[i]; results.push({ id: list[i].getAttribute('Id') || '', target: item.getAttribute('Target') || '', path: '' }) } const PromiseList = results.map(item => { return analysisImageBase64(zipLoadResultFiles, item.target) }) await Promise.all(PromiseList).then(res => { res.forEach((item, index) => { results[index].path = item; }) }) return results } // 將圖片解析為base const analysisImageBase64 = async (zipLoadResultFiles: { [x: string]: JSZipObject }, keys: string) => { const imageKey = keys.replace('..', 'xl'); const fileContent = await zipLoadResultFiles[imageKey].async('base64'); const url = `data:image/png;base64,${fileContent}`; return url; } // 解析圖示座標 const analysisImageLocation = async (zipLoadResultFiles: { [x: string]: JSZip.JSZipObject }, imageList: imageList) => { const imageLocationKey = 'xl/drawings/drawing1.xml';//圖片座標檔案路徑 const fileContent = await zipLoadResultFiles[imageLocationKey].async('string'); let parser = new DOMParser(); let xmldom = parser.parseFromString(fileContent, "text/xml"); // col單元格 let colList = xmldom.getElementsByTagName("xdr:col"); // row單元格 let rowList = xmldom.getElementsByTagName("xdr:row"); // 圖片 let blip = xmldom.getElementsByTagName("a:blip"); let locationList = [] as { form: { x: string, y: string }, to: { x: string, y: string }, id: string, path: string }[]; for (var i = 0; i < blip.length; i++) { const formX = colList[i * 2].textContent || ''; const toX = colList[i * 2 + 1].textContent || ''; const formY = rowList[i * 2].textContent || ''; const toY = rowList[i * 2 + 1].textContent || ''; const id = blip[i].getAttribute('r:embed'); const path = imageList.filter(i => i.id == id)[0].path locationList.push({ form: { x: formX, y: formY }, to: { x: toX, y: toY }, id: blip[i].getAttribute('r:embed') || '', path }); } return locationList } // 獲取圖片 async function getExcelImage(file: any) { let result: any[] = []; // 用來存放圖片 const zip = new JSZip(); // 建立jszip例項 try { let zipLoadResult = await zip.loadAsync(file); // 將xlsx檔案轉zip檔案 const zipLoadResultFiles = zipLoadResult["files"]; const imageList = await analysisImageList(zipLoadResultFiles); result = await analysisImageLocation(zipLoadResultFiles, imageList); } catch (error) { console.log(error); } return result; } </script> <style lang="scss" scoped></style>
原理就是透過jszip將excel解壓,解壓後的檔案目錄為
透過檢視裡面檔案可以找到記錄圖片座標,記錄圖片的xml,還有圖片的路徑
使用DOMparser解析出xml檔案,獲取需要的資料,在計算出圖片所在的位置即可