fix: adds Excel export support for SEP list data
This commit is contained in:
@@ -26,7 +26,7 @@ import type { VclaimSepData } from '~/models/vclaim'
|
||||
|
||||
// Libraries
|
||||
import { getFormatDateId } from '~/lib/date'
|
||||
import { downloadCsv } from '~/lib/download'
|
||||
import { downloadCsv, downloadXls } from '~/lib/download'
|
||||
|
||||
// Constants
|
||||
import { serviceTypes } from '~/lib/constants.vclaim'
|
||||
@@ -184,9 +184,20 @@ function exportCsv() {
|
||||
downloadCsv(headers, data.value, filename)
|
||||
}
|
||||
|
||||
function exportExcel() {
|
||||
console.log('Ekspor Excel dipilih')
|
||||
// tambahkan logic untuk generate Excel
|
||||
async function exportExcel() {
|
||||
if (!data.value || data.value.length === 0) {
|
||||
toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const headers = Object.keys(data.value[0] || {})
|
||||
const filename = `file-sep-${getFormatDateId(today)}.xlsx`
|
||||
await downloadXls(headers, data.value, filename, 'SEP Data')
|
||||
} catch (err: any) {
|
||||
console.error('exportExcel error', err)
|
||||
toast({ title: 'Gagal', description: err?.message || 'Gagal mengekspor data ke Excel', variant: 'destructive' })
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRemove() {
|
||||
|
||||
+78
-9
@@ -6,16 +6,14 @@
|
||||
* @param filename - optional file name to use for downloaded file
|
||||
* @param delimiter - csv delimiter (default is comma)
|
||||
* @param addBOM - add UTF-8 BOM to the file to make Excel detect UTF-8 correctly
|
||||
* Usage examples:
|
||||
* 1) With headers and array of objects
|
||||
* downloadCsv(['name', 'age'], [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
|
||||
* 2) Without headers (automatically uses object keys)
|
||||
* downloadCsv(null, [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
|
||||
* 3) With array-of-arrays
|
||||
* downloadCsv(['col1', 'col2'], [['a', 'b'], ['c', 'd']], 'matrix.csv');
|
||||
*/
|
||||
/*
|
||||
Usage examples:
|
||||
// 1) With headers and array of objects
|
||||
// downloadCsv(['name', 'age'], [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
|
||||
// 2) Without headers (automatically uses object keys)
|
||||
// downloadCsv(null, [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
|
||||
// 3) With array-of-arrays
|
||||
// downloadCsv(['col1', 'col2'], [['a', 'b'], ['c', 'd']], 'matrix.csv');
|
||||
*/
|
||||
export function downloadCsv(
|
||||
headers: string[] | null,
|
||||
data: Array<Record<string, any> | any[]>,
|
||||
@@ -79,3 +77,74 @@ export function downloadCsv(
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* Download data as XLS (Excel) file using xlsx library.
|
||||
*
|
||||
* @param headers - Array of header names. If omitted and data is array of objects, keys will be taken from first object.
|
||||
* @param data - Array of rows. Each row can be either an object (key -> value) or an array of values.
|
||||
* @param filename - optional file name to use for downloaded file (default: 'data.xlsx')
|
||||
* @param sheetName - optional sheet name in workbook (default: 'Sheet1')
|
||||
* Usage examples:
|
||||
* 1) With headers and array of objects
|
||||
* await downloadXls(['name', 'age'], [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.xlsx');
|
||||
* 2) Without headers (automatically uses object keys)
|
||||
* await downloadXls(null, [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.xlsx');
|
||||
* 3) With custom sheet name
|
||||
* await downloadXls(['col1', 'col2'], [['a', 'b'], ['c', 'd']], 'matrix.xlsx', 'MyData');
|
||||
*/
|
||||
export async function downloadXls(
|
||||
headers: string[] | null,
|
||||
data: Array<Record<string, any> | any[]>,
|
||||
filename = 'data.xlsx',
|
||||
sheetName = 'Sheet1',
|
||||
) {
|
||||
// Dynamically import xlsx to avoid server-side issues
|
||||
const { utils, write } = await import('xlsx')
|
||||
const { saveAs } = await import('file-saver')
|
||||
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
// Create empty sheet with headers only
|
||||
const ws = utils.aoa_to_sheet(headers ? [headers] : [[]])
|
||||
const wb = utils.book_new()
|
||||
utils.book_append_sheet(wb, ws, sheetName)
|
||||
const wbout = write(wb, { bookType: 'xlsx', type: 'array' })
|
||||
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename)
|
||||
return
|
||||
}
|
||||
|
||||
// if headers not provided and rows are objects, take keys from first object
|
||||
let _headers: string[] | null = headers
|
||||
if (!_headers) {
|
||||
const firstRow = data[0]
|
||||
if (typeof firstRow === 'object' && !Array.isArray(firstRow)) {
|
||||
_headers = Object.keys(firstRow)
|
||||
} else if (Array.isArray(firstRow)) {
|
||||
_headers = null
|
||||
}
|
||||
}
|
||||
|
||||
// Convert data rows to 2D array
|
||||
const rows: any[][] = data.map((row) => {
|
||||
if (Array.isArray(row)) {
|
||||
return row
|
||||
}
|
||||
// object row - map using headers if available, otherwise use object values
|
||||
if (_headers && Array.isArray(_headers)) {
|
||||
return _headers.map((h) => (row as Record<string, any>)[h] ?? '')
|
||||
}
|
||||
return Object.values(row)
|
||||
})
|
||||
|
||||
// Combine headers and rows for sheet
|
||||
const sheetData = _headers ? [_headers, ...rows] : rows
|
||||
|
||||
// Create worksheet and workbook
|
||||
const ws = utils.aoa_to_sheet(sheetData)
|
||||
const wb = utils.book_new()
|
||||
utils.book_append_sheet(wb, ws, sheetName)
|
||||
|
||||
// Write and save file
|
||||
const wbout = write(wb, { bookType: 'xlsx', type: 'array' })
|
||||
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user