Vue純前端實現(xiàn)導出excel中的圖片與文件超鏈接
最近公司的需求,要求Excel導出的時候?qū)?shù)據(jù)里的圖片、文件等都帶出來。
一頓溝通后,后端不做,那就只好我這個前端小白來了。上網(wǎng)搜索了好久,有很多大佬分享了怎么導出圖片,但沒有將圖片按單元格級批量導出的例子,只好研究了一整天時間,實現(xiàn)效果如下:
這是vue展示的樣式:

這是excel導出的樣式:

下面直接上代碼: 具體需要注意的部分在下面說
首先npm 安裝依賴: exceljs
數(shù)據(jù):(主要是展示el table ,順便也可以作為excel 導出的列模版)
data() {
return {
tableProp: [
{
prop: 'creator',
label: '提出人',
namei18n: 'tichucreator',
width: 100,
align: 'center'
},
{
label: '提出時間',
prop: 'creationTime',
namei18n: 'creationTime',
hidden: true
},
{
prop: 'projectCode',
label: '項目編碼',
namei18n: 'projectCode',
width: 150,
align: 'center'
},
{
prop: 'projectName',
label: '項目名稱',
namei18n: 'projectName',
width: 240,
align: 'center'
},
{
prop: 'stationCode',
label: '工位',
namei18n: 'stationCode',
width: 150,
align: 'center'
},
{
label: '負責人',
prop: 'handler',
namei18n: 'handler',
hidden: true
},
{
prop: 'questionType',
label: '問題類型',
namei18n: 'questionType',
width: 150,
align: 'center'
},
{
prop: 'importance',
label: '重要程度',
namei18n: 'importance',
width: 100,
align: 'center',
options: []
},
{
prop: 'stage',
label: '階段',
namei18n: 'stage',
width: 150,
align: 'center',
options: []
},
{
label: '開始處理時間',
prop: 'startDate',
namei18n: 'startDate1',
hidden: true
},
{
label: '實際完成時間',
prop: 'lastModificationTime',
namei18n: 'lastModificationTime',
hidden: true
},
{
prop: 'questionDescribe',
label: '問題描述',
namei18n: 'questionDescribe',
width: 200,
align: 'center'
},
{
prop: 'uploadPic',
label: '圖片描述',
namei18n: 'uploadPic',
type: 'uploadPic',
width: 330,
align: 'center'
},
{
prop: 'uploadFile',
label: '附件描述',
namei18n: 'uploadFile',
type: 'uploadFile',
width: 300,
align: 'center'
},
{
prop: 'questionAdvise',
label: '問題解決建議',
namei18n: 'questionAdvise',
width: 200,
align: 'center'
},
{
label: '補充說明',
prop: 'bRemarks',
namei18n: 'bRemarks',
hidden: true
},
{
label: '解決方案',
prop: 'solution',
namei18n: 'solution',
hidden: true
},
{
prop: 'expectedDate',
label: '需求完成時間',
namei18n: 'expectedDate',
width: 160,
align: 'center'
},
{
prop: 'isOverdue',
label: '是否逾期',
namei18n: 'isOverdue',
width: 100,
align: 'center',
options: [
{
value: true,
label: '是'
},
{
value: false,
label: '否'
}
]
// formatter: (val) => {
// if (val) {
// return '是';
// } else {
// return '否';
// }
// }
},
],
},方法:
methods: {
async exportExcel1(exportDataList) {
//exportDataList 就是后端返回的數(shù)據(jù)
exportDataList = this.tableDataFormat(JSON.parse(JSON.stringify(exportDataList)));
// 定義表頭
const columns = [];
// this.tableProp 在上面有粘出來,主要是表格的表頭配置,el-table就是用這個循環(huán)出來的
// 解釋一下這里的操作,excel并不支持同一單元格導出多個數(shù)據(jù)(也可能是我太菜)
// 我們的項目是最多3個圖 3個文件,后端返回的是3個文件路徑的字符串在一個字段里
this.tableProp.forEach((item) => {
if (item.label != '圖片描述' && item.label != '附件描述') {
columns.push({
header: item.label,
key: item.prop,
width: item.width ? item.width / 10 : 14
});
}
// 所以將 圖片 和 文件變?yōu)?3項,也就是在excel里占 3個 單元格
if (item.label === '圖片描述') {
columns.push({
header: '',
key: 'uploadPic1',
width: 14
});
columns.push({
header: '',
key: 'uploadPic2',
width: 14
});
columns.push({
header: '',
key: 'uploadPic3',
width: 14
});
}
if (item.label === '附件描述') {
columns.push({
header: '',
key: 'uploadFile1',
width: 14
});
columns.push({
header: '',
key: 'uploadFile2',
width: 14
});
columns.push({
header: '',
key: 'uploadFile3',
width: 14
});
}
});
const Exceljs = require('exceljs');
// 創(chuàng)建工作簿
const workbook = new Exceljs.Workbook();
// 創(chuàng)建工作表
const workSheet = workbook.addWorksheet('sheet1');
// 工作表添加表頭
workSheet.columns = columns;
// imgFieldList:圖片字段名
const imgFieldList = ['uploadPic1', 'uploadPic2', 'uploadPic3'];
// fileFieldList:文件字段名
const fileFieldList = ['uploadFile1', 'uploadFile2', 'uploadFile3'];
// 往工作表插入數(shù)據(jù) (插入圖片后,鏈接也會被顯示,所以把圖片的數(shù)據(jù)干掉)
workSheet.addRows(
exportDataList.map((item) => {
return {
...item,
uploadPic1: '',
uploadPic2: '',
uploadPic3: ''
// uploadFile1: '',
// uploadFile2: '',
// uploadFile3: ''
};
})
);
// 往Excel插入圖片
for (let ri = 0; ri < exportDataList.length; ri++) {
// 獲取遍歷的當前行
const row = exportDataList[ri];
// 過濾出當前行圖片字段有值的
const currentRowImgFieldList = imgFieldList.filter((e) => row[e]);
// 遍歷圖片字段
for (let ai = 0; ai < currentRowImgFieldList.length; ai++) {
const imgField = currentRowImgFieldList[ai];
// 圖片字段值,一個完整的url
const url = row[imgField];
// 根據(jù)url把圖片轉(zhuǎn)換成base64編碼,這里加了await, 方法名前面必須得加async,把這個imageToBase64方法變成同步方法
const base64 = await this.imageToBase64(url);
// 把base64編碼的圖片插入excel工作簿里面
const imageId = workbook.addImage({
base64: base64,
extension: 'png'
});
// 當前工作表(當前excel頁)加入圖片,tl.col:excel第幾列,tl.row:excel第幾行,ext里面表示圖片寬高
// 這里需要細細調(diào)一下,加入單元格后是緊挨著單元格頂部,要做一些偏移
workSheet.addImage(imageId, {
tl: { col: columns.length + ai - 11.8, row: ri + 1.2 },
// br: { col: columns.length + ai - 11.1, row: ri + 2 }
ext: { width: 104, height: 73 }
});
}
}
for (let ri = 0; ri < exportDataList.length; ri++) {
// 設置除了標題之外,內(nèi)容的行高,避免圖片太高,這里太小,導致顯示很亂
workSheet.getRow(ri + 2).height = 60;
}
// 設置標題列的高度
workSheet.getRow(1).height = 20;
// 設置整個工作表的樣式
const font = {
name: '微軟雅黑',
size: 10 // 字體大小
};
const font1 = {
name: '宋體',
size: 10, // 字體大小
bold: true,
color: {
argb: '00000002'
}
};
const border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
const alignment = {
horizontal: 'center',
vertical: 'middle'
};
const headerFillColor = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'F0F0F0F0' } // 淺灰色背景,您可以自定義顏色
};
for (let rowIndex = 1; rowIndex <= workSheet.actualRowCount; rowIndex++) {
for (let colIndex = 1; colIndex <= workSheet.columnCount; colIndex++) {
const cell = workSheet.getCell(rowIndex, colIndex);
cell.font = font;
cell.border = border;
cell.alignment = alignment;
// 如果是附件 (這里主要是文件的邏輯)
if ((colIndex === 16 || colIndex === 17 || colIndex === 18) && rowIndex !== 1) {
cell.font = {
underline: true,
color: { argb: 'FF1890FF' }
};
if (cell.value) {
cell.value = {
text: cell.value.split('/')[cell.value.split('/').length - 1],
hyperlink: window.location.origin + cell.value,
tooltip: '點擊跳轉(zhuǎn)'
};
}
}
// 如果是第一行,設置背景顏色
if (rowIndex === 1) {
cell.fill = headerFillColor;
cell.font = font1;
}
}
// 合并圖片單元格
workSheet.mergeCells('M' + rowIndex + ':O' + rowIndex);
}
// 合并文件單元格
workSheet.mergeCells('P1:R1');
// 添加一些數(shù)據(jù)到合并后的單元格
workSheet.getCell('M1').value = '圖片描述';
// 添加一些數(shù)據(jù)到合并后的單元格
workSheet.getCell('P1').value = '附件描述';
// 工作簿寫入excel
workbook.xlsx.writeBuffer().then((buffer) => {
// // 轉(zhuǎn)換成Blob格式
// const blob = new Blob([buffer], { type: 'application/octet-stream' });
// // 導出excel,這里獲得了blob,有很多種導出方法,可以用FileSaver.js(百度一下就有了),我這里就簡單點了,用HTML的A標簽導出
// // 獲取下載鏈接
// const url = URL.createObjectURL(blob);
// // 動態(tài)創(chuàng)建a標簽
// let downloadLink = document.createElement('a');
// downloadLink.style.display = 'none'; // 隱藏這個a標簽
// document.body.appendChild(downloadLink); // 添加到DOM中
// // 設置下載鏈接和文件名
// downloadLink.href = url;
// downloadLink.download = '問題匯總導出.xlsx'; // 自定義下載時的文件名
// // 觸發(fā)點擊事件來開始下載
// downloadLink.click();
// // 下載后移除這個a標簽
// document.body.removeChild(downloadLink);
// 使用saveAs下載 不會報出文件不安全
saveAs(new Blob([buffer], { type: 'application/octet-stream' }), "問題匯總導出.xlsx")
});
this.loading.close();
},
imageToBase64(url, width, height) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = url;
image.crossOrigin = '*';
image.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = width || image.width;
canvas.height = height || image.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width || image.width, height || image.height);
const base64 = canvas.toDataURL('image/png');
resolve(base64);
};
});
},
tableDataFormat(exportlist) {
exportlist.forEach((item) => {
// 這里是測試數(shù)據(jù),分割是;,要是用這個測試的話要改一下下面的分割符 和 文件后綴
// item.annexUrl = 'https://img2.baidu.com/it/u=101236606,1785081092&fm=253&fmt=auto&app=138&f=JPEG?w=727&h=500;https://img1.baidu.com/it/u=2607921640,3248579257&fm=253&fmt=auto&app=138&f=JPEG?w=629&h=500;https://img1.baidu.com/it/u=2976068608,3323494935&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500'
// 這里是后端返回id,處理成字符串,大家未必用得上
this.tableProp.forEach((val) => {
if (val.options && val.options.length > 0) {
val.options.forEach((item1) => {
if (item1.value == item[val.prop]) {
item[val.prop] = item1.label;
}
});
}
});
item.uploadPic = [];
item.uploadPic1 = '';
item.uploadPic2 = '';
item.uploadPic3 = '';
item.uploadFile = [];
item.uploadFile1 = '';
item.uploadFile2 = '';
item.uploadFile3 = '';
if (item.annexUrl) {
let fileArr = item.annexUrl.split(',');
let filetype = ['.doc', '.docx', '.pdf', '.vsdx', '.ppt', '.pptx', '.xlsx', '.xls', '.xmind', '.zip', '.rar', '.7z'];
// 移除圖片類型以避免與圖片類型的判斷重疊,并統(tǒng)一轉(zhuǎn)換為小寫
const exclusiveFiletype = filetype.map((type) => type.toLowerCase()).filter((type) => !['.png', '.jpg', '.jpeg', '.gif'].map((ext) => ext.toLowerCase()).includes(type));
fileArr.forEach((fileUrl) => {
// 將文件URL轉(zhuǎn)換為小寫,以便進行不區(qū)分大小寫的比較
const lowerCaseFileUrl = fileUrl.toLowerCase();
// 先檢查是否為圖片類型
['.png', '.jpg', '.jpeg', '.gif'].forEach((type) => {
const lowerCaseType = type.toLowerCase();
if (lowerCaseFileUrl.endsWith(lowerCaseType)) {
item.uploadPic.push(fileUrl);
}
});
// 然后檢查是否為其他文件類型
exclusiveFiletype.forEach((type) => {
if (lowerCaseFileUrl.endsWith(type)) {
item.uploadFile.push(fileUrl);
}
});
});
if (item.uploadPic.length > 0) {
item.uploadPic1 = item.uploadPic[0];
item.uploadPic2 = item.uploadPic[1] || '';
item.uploadPic3 = item.uploadPic[2] || '';
}
if (item.uploadFile.length > 0) {
item.uploadFile1 = item.uploadFile[0];
item.uploadFile2 = item.uploadFile[1] || '';
item.uploadFile3 = item.uploadFile[2] || '';
}
}
});
return exportlist;
},
}因為 excel 插件都是按單元格級別的,圖片、超鏈接等如果想導出多個,就要像我上面那樣一系列復雜操作,如果一行只有一個的話,會簡單很多!代碼肯定是可以運行的,復制即可使用,就是要調(diào)整一下字段,對應上你自己項目的真實返回數(shù)據(jù)
到此這篇關于Vue純前端實現(xiàn)導出excel中的圖片與文件超鏈接的文章就介紹到這了,更多相關Vue導出excel內(nèi)容內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
頁面內(nèi)錨點定位及跳轉(zhuǎn)方法總結(推薦)
這篇文章主要介紹了頁面內(nèi)錨點定位及跳轉(zhuǎn)方法總結,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-04-04
vue2.x element-ui實現(xiàn)pc端購物車頁面demo
這篇文章主要為大家介紹了vue2.x element-ui實現(xiàn)pc端購物車頁面demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06
Vue+springboot批量刪除功能實現(xiàn)代碼
這篇文章主要介紹了Vue+springboot批量刪除功能,本文通過示例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-05-05

