I'm using PrimeVue's DataTable component in a Nuxt 3 project, and I'm trying to export the table data to an XLSX file. The primary reason for choosing this file format is to be able to include images in the cells.
import * as XLSX from 'xlsx';
function exportCSVHandler() {
const data = props.data;
const fields = selectedColumns.value.map((col) => col.field);
const filteredData = data.map((item) => {
const filteredItem = {};
fields.forEach((field) => {
filteredItem[field] = item[field];
});
return filteredItem;
});
if (dataTableRef.value) {
const ws = XLSX.utils.json_to_sheet(filteredData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet 1');
const fileName = 'exported_data.xlsx';
XLSX.writeFile(wb, fileName);
}
}
Here is the data table
<Button label="download" @click="exportCSVHandler" />
<DataTable
ref="dataTableRef"
v-model:filters="filters"
:value="filteredData"
show-gridlines
paginator
responsive-layout="scroll"
:rows="5"
:row-hover="true"
data-key="id"
filter-display="menu"
overlay-visible
striped-rows
csv-separator
:global-filter-fields="['filled_on', 'text_filled']"
paginator-template="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
current-page-report-template="Showing {first} to {last} of {totalRecords} entries"
@update:filters="onFilterChange">
<template #header>
<DataTableFilters
:filters="filters"
:hasFilterActions="props.hasFilterActions"
:typefilter="typefilter"
@filterData="filterData"
@clearFilter="clearFilter"
/>
<div class="text-left mt-7 w-3/4 sm:w-full text-sm">
<MultiSelect
v-model="selectedColumns"
:options="columns"
optionLabel="header"
display="chip"
placeholder="Select Columns"
/>
</div>
</template>
<template #empty>
Select a template to display data.
</template>
<template #loading>
Loading data. Please wait.
</template>
<Column
v-for="(column, index) of selectedColumns"
:field="column.field"
:header="column.header"
:key="column.field + '_' + index"
:data-type="column.data_type"
:style="column.style"
:filter-field="column.filterField"
:show-filter-match-modes="column.showFilterMatchModes"
:filter-menu-style="{ width: '14rem' }"
>
<template #body="{ data }">
<div v-if="column.header === 'Image'">
<div class=" flex justify-content-center">
<Button icon="pi pi-eye" outlined text @click="toggleDialog(index, data[column.field])"/>
</div>
</div>
<div v-else-if="column.header === 'Filled On'">
<i class="pi pi-calendar text-primaryBlue font-bold mr-4 text-xl"></i>
{{ formatDate(data[column.field]) }}
</div>
<div v-else class="flex ">
{{ data[column.field] }}
</div>
</template>
<template v-if="column.data_type === 'date'" #filter="{ filterModel }">
<Calendar
v-model="filterModel.value"
date-format="mm/dd/yy"
placeholder="mm/dd/yyyy"
mask="99/99/9999"
/>
</template>
<template v-else-if="column.header !== 'Image'" #filter="{ filterModel }">
<InputText
v-model="filterModel.value"
type="text"
class="p-column-filter"
:placeholder="getPlaceholder(column.header)"
/>
</template>
</Column>
</DataTable>
Here is the data format
const data = [
{
id: 6,
filled_on: new Date('2024-04-22'),
image: 'https://assets-global.website-files.com/5ebb0930dd82631397ddca92/61b8ef1f82c1b3167fe39e0a_product-template-forms-finance.png',
text_filled: 'Sed Do Eiusmod',
type: 'Paper',
templateName: 'Temp1',
},
]
I tried different libraries and converting image links to base64(ERROR: text length must not exceed 32767 characters), I haven't been successful.