207 lines
6.0 KiB
Plaintext
207 lines
6.0 KiB
Plaintext
ExcelJS Spreadsheet Guide: Reading, Writing, Formatting, and VFS Integration
|
|
|
|
# Goal
|
|
Provide actionable instructions, code patterns, and best practices for creating, reading, modifying, and styling Excel (`.xlsx`) and CSV spreadsheets in the `javascript_execute` sandbox using `ExcelJS`.
|
|
|
|
# Environment & Globals
|
|
- Both `ExcelJS` and `exceljs` are pre-injected in global scope. Do NOT use `require()` or `import`.
|
|
- `Workbook`, `stream.xlsx.WorkbookWriter`, and `stream.xlsx.WorkbookReader` are pre-bridged to the Virtual Filesystem (VFS).
|
|
- Wrap async scripts in an IIFE: `module.exports = (async () => { ... })();`.
|
|
|
|
---
|
|
|
|
# 1. Creating and Populating a Workbook
|
|
|
|
```javascript
|
|
module.exports = (async () => {
|
|
const wb = new ExcelJS.Workbook();
|
|
wb.creator = 'QuixPOS Assistant';
|
|
wb.created = new Date();
|
|
|
|
const ws = wb.addWorksheet('Articles', {
|
|
properties: { tabColor: { argb: 'FF1F4E79' } },
|
|
views: [{ state: 'frozen', xSplit: 0, ySplit: 1 }] // freeze header row
|
|
});
|
|
|
|
// Define column schemas
|
|
ws.columns = [
|
|
{ header: 'SKU', key: 'sku', width: 14 },
|
|
{ header: 'Title', key: 'title', width: 36 },
|
|
{ header: 'Price (Gross)', key: 'price', width: 14, style: { numFmt: '#,##0.00 €' } },
|
|
{ header: 'Stock', key: 'stock', width: 10, style: { numFmt: '#,##0' } },
|
|
{ header: 'Supplier SKU', key: 'supplierSku', width: 16 }
|
|
];
|
|
|
|
// Add rows via object matching column keys
|
|
ws.addRow({ sku: 'ART-1001', title: 'Espresso Arabica Premium', price: 14.99, stock: 45, supplierSku: 'SUP-01' });
|
|
ws.addRow({ sku: 'ART-1002', title: 'Earl Grey Organic Tea', price: 8.50, stock: 20, supplierSku: 'SUP-02' });
|
|
|
|
// Or add multiple rows at once
|
|
ws.addRows([
|
|
{ sku: 'ART-1003', title: 'Single Origin Colombia', price: 16.20, stock: 12, supplierSku: 'SUP-03' },
|
|
{ sku: 'ART-1004', title: 'Matcha Ceremonial Grade', price: 24.00, stock: 8, supplierSku: 'SUP-04' }
|
|
]);
|
|
|
|
// Save directly to VFS
|
|
await wb.xlsx.writeFile('articles-export.xlsx');
|
|
|
|
return { success: true, rows: ws.rowCount, file: 'articles-export.xlsx' };
|
|
})();
|
|
```
|
|
|
|
---
|
|
|
|
# 2. Header & Cell Styling
|
|
|
|
```javascript
|
|
// Header styling: bold white text with dark blue background
|
|
const headerRow = ws.getRow(1);
|
|
headerRow.height = 24;
|
|
headerRow.font = { name: 'Arial', size: 11, bold: true, color: { argb: 'FFFFFFFF' } };
|
|
headerRow.fill = {
|
|
type: 'pattern',
|
|
pattern: 'solid',
|
|
fgColor: { argb: 'FF1F4E79' }
|
|
};
|
|
headerRow.alignment = { vertical: 'middle', horizontal: 'center' };
|
|
|
|
// Zebra striping data rows
|
|
for (let i = 2; i <= ws.rowCount; i++) {
|
|
const row = ws.getRow(i);
|
|
row.height = 20;
|
|
row.alignment = { vertical: 'middle' };
|
|
if (i % 2 === 0) {
|
|
row.fill = {
|
|
type: 'pattern',
|
|
pattern: 'solid',
|
|
fgColor: { argb: 'FFF9FAFB' }
|
|
};
|
|
}
|
|
// Thin borders
|
|
row.eachCell({ includeEmpty: true }, (cell) => {
|
|
cell.border = {
|
|
bottom: { style: 'thin', color: { argb: 'FFE5E7EB' } }
|
|
};
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# 3. Formulas & Summary Rows
|
|
|
|
```javascript
|
|
// Add a total row at the end
|
|
const totalRow = ws.addRow({
|
|
title: 'Total Stock & Value',
|
|
price: { formula: 'SUMPRODUCT(C2:C5, D2:D5)', result: 0 },
|
|
stock: { formula: 'SUM(D2:D5)', result: 85 }
|
|
});
|
|
|
|
totalRow.font = { bold: true };
|
|
totalRow.getCell('title').alignment = { horizontal: 'right' };
|
|
totalRow.border = {
|
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
|
bottom: { style: 'double', color: { argb: 'FF000000' } }
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
# 4. Reading & Modifying Existing Spreadsheets from VFS
|
|
|
|
```javascript
|
|
module.exports = (async () => {
|
|
const wb = new ExcelJS.Workbook();
|
|
// Path is relative to VFS (e.g. user attachment or previous artifact)
|
|
await wb.xlsx.readFile('attachments/inventory.xlsx');
|
|
|
|
const ws = wb.getWorksheet(1); // 1-based index or name 'Sheet1'
|
|
const records = [];
|
|
|
|
ws.eachRow({ includeEmpty: false }, (row, rowNumber) => {
|
|
if (rowNumber === 1) return; // skip header
|
|
records.push({
|
|
sku: row.getCell(1).text,
|
|
name: row.getCell(2).text,
|
|
price: Number(row.getCell(3).value) || 0,
|
|
stock: Number(row.getCell(4).value) || 0
|
|
});
|
|
});
|
|
|
|
// You can modify cells and write back
|
|
ws.getCell('C2').value = 19.99;
|
|
await wb.xlsx.writeFile('attachments/inventory-updated.xlsx');
|
|
|
|
return { totalRead: records.length, records };
|
|
})();
|
|
```
|
|
|
|
---
|
|
|
|
# 5. Working with CSV Files
|
|
|
|
```javascript
|
|
// Read CSV from VFS
|
|
const csvWb = new ExcelJS.Workbook();
|
|
await csvWb.csv.readFile('data.csv');
|
|
const sheet = csvWb.getWorksheet(1);
|
|
|
|
// Write CSV to VFS
|
|
await csvWb.csv.writeFile('export.csv');
|
|
```
|
|
|
|
---
|
|
|
|
# 6. Streaming Massive Datasets
|
|
|
|
For large tables (>5,000 rows) use `WorkbookWriter` to prevent high memory consumption:
|
|
|
|
```javascript
|
|
module.exports = (async () => {
|
|
const writer = new ExcelJS.stream.xlsx.WorkbookWriter({ filename: 'large-catalog.xlsx' });
|
|
const ws = writer.addWorksheet('Catalog');
|
|
|
|
ws.columns = [
|
|
{ header: 'ID', key: 'id', width: 10 },
|
|
{ header: 'Name', key: 'name', width: 30 },
|
|
{ header: 'Stock', key: 'stock', width: 12 }
|
|
];
|
|
|
|
for (let i = 1; i <= 10000; i++) {
|
|
ws.addRow({ id: i, name: `Product ${i}`, stock: Math.floor(Math.random() * 100) }).commit();
|
|
}
|
|
|
|
ws.commit();
|
|
await writer.commit();
|
|
|
|
return { exportedRows: 10000, file: 'large-catalog.xlsx' };
|
|
})();
|
|
```
|
|
|
|
---
|
|
|
|
# 7. Embedding Product / Company Images
|
|
|
|
```javascript
|
|
// Image filename is automatically resolved from VFS
|
|
const logoId = wb.addImage({
|
|
filename: 'images/logo.png',
|
|
extension: 'png'
|
|
});
|
|
|
|
// Position image by cell coordinates
|
|
ws.addImage(logoId, {
|
|
tl: { col: 5, row: 1 },
|
|
ext: { width: 120, height: 40 }
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
# 8. Presenting Output to User
|
|
|
|
When you generate or modify an Excel file in `javascript_execute`, **always include a clickable Markdown link in your response summary**:
|
|
- `[Download Articles Spreadsheet](articles-export.xlsx)`
|
|
- `[Download Updated Inventory](attachments/inventory-updated.xlsx)`
|