nexabase-report 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -172
- package/dist/{html2canvas-DTMBknrb.js → html2canvas-Bz1P596e.js} +1 -1
- package/dist/{html2canvas-d7a22Xlx.js → html2canvas-CO3zd_po.js} +2 -2
- package/dist/{html2pdf-CUIndHPV.js → html2pdf-BKFAbMM9.js} +3 -3
- package/dist/{index-B77DEEx0.js → index-CrlafZ0y.js} +60 -5
- package/dist/{index.es-B-paKwhr.js → index.es-Dn-NWvkn.js} +2 -2
- package/dist/{jspdf.es.min-Bq3BwrOB.js → jspdf.es.min-DEtCEkBC.js} +2 -2
- package/dist/nexabase-report.es.js +1 -1
- package/dist/nexabase-report.umd.js +2 -2
- package/docs/PUBLISHING.md +22 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/nexabase-report)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://vuejs.org/)
|
|
7
7
|
|
|
8
|
-
> Librería Vue 3 + TypeScript para diseñar y visualizar reportes tipo banded (inspirada en Stimulsoft / DevExpress). Incluye **diseñador WYSIWYG
|
|
8
|
+
> Librería Vue 3 + TypeScript para diseñar y visualizar reportes tipo banded (inspirada en Stimulsoft / DevExpress). Incluye **diseñador WYSIWYG** con multilingüe (ES/EN/PT), **visor framework-agnostic** como Custom Element, y **exportación 100% cliente** a PDF vectorial, Excel, Word y CSV.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
- [Características](#características)
|
|
15
15
|
- [Instalación](#instalación)
|
|
16
16
|
- [Uso rápido](#uso-rápido)
|
|
17
|
-
- [Diseñador
|
|
17
|
+
- [Diseñador (NexaDesigner)](#diseñador-nexadesigner)
|
|
18
18
|
- [API del Viewer](#api-del-viewer)
|
|
19
19
|
- [Formato de datos](#formato-de-datos)
|
|
20
20
|
- [Variables y expresiones](#variables-y-expresiones)
|
|
21
|
-
- [Ejemplos
|
|
21
|
+
- [Ejemplos](#ejemplos)
|
|
22
22
|
- [Documentación adicional](#documentación-adicional)
|
|
23
|
+
- [Publicación](#publicación)
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -28,17 +29,21 @@
|
|
|
28
29
|
| Característica | Descripción |
|
|
29
30
|
|----------------|-------------|
|
|
30
31
|
| **Custom Element** | `<nexa-viewer>` funciona en Vue, React, Angular, Svelte, Blazor o HTML puro |
|
|
31
|
-
| **Diseñador WYSIWYG** | `NexaDesigner.vue` —
|
|
32
|
-
| **Visor standalone** | `<nexa-viewer>` —
|
|
33
|
-
| **Exportación cliente** | PDF vectorial (jsPDF), Excel (SheetJS), Word (docx), CSV |
|
|
34
|
-
| **Gráficos** | ECharts integrado (barras, líneas, pastel,
|
|
35
|
-
| **Tablas dinámicas** | Crosstabs nativos con agregaciones |
|
|
36
|
-
| **Códigos QR / Barras** | jsbarcode + qrcode |
|
|
37
|
-
| **Formato condicional** | Reglas visuales por campo y operador |
|
|
38
|
-
| **Master-Detail** |
|
|
39
|
-
| **
|
|
40
|
-
| **
|
|
41
|
-
| **
|
|
32
|
+
| **Diseñador WYSIWYG** | `NexaDesigner.vue` — lienzo, ribbon, sidebars, multilingüe ES/EN/PT |
|
|
33
|
+
| **Visor standalone** | `<nexa-viewer>` — sin Vue en el proyecto consumidor |
|
|
34
|
+
| **Exportación 100% cliente** | PDF vectorial (jsPDF), Excel (SheetJS), Word (docx), CSV (UTF-8 BOM) |
|
|
35
|
+
| **Gráficos** | ECharts integrado (barras, líneas, pastel, área, scatter, radar, etc.) |
|
|
36
|
+
| **Tablas dinámicas** | Crosstabs nativos con agregaciones (sum, count, avg, min, max) |
|
|
37
|
+
| **Códigos QR / Barras** | jsbarcode + qrcode con bindings a datos |
|
|
38
|
+
| **Formato condicional** | Reglas visuales por campo y operador (eq, gt, contains, between, etc.) |
|
|
39
|
+
| **Master-Detail** | Relaciones padre-hijo entre bandas con filtro automático |
|
|
40
|
+
| **Drill-Down** | Navegación a reportes destino con paso de parámetros |
|
|
41
|
+
| **Subreportes** | Anidamiento de reportes desde definición embebida o remota |
|
|
42
|
+
| **Motor de expresiones** | Seguro (sin `eval`): `FormatNumber`, `FormatDate`, `IIF`, `ISNULL`, agregaciones |
|
|
43
|
+
| **Parámetros** | Diálogo de entrada, valores por props, `skipParamsDialog` |
|
|
44
|
+
| **Paginación** | Automática por altura de página con thumbnails y navegación |
|
|
45
|
+
| **Shapes** | Rectángulos, elipses, líneas y flechas con estilo configurable |
|
|
46
|
+
| **i18n** | Interfaz multilingüe del diseñador: español, inglés, portugués |
|
|
42
47
|
|
|
43
48
|
---
|
|
44
49
|
|
|
@@ -64,23 +69,25 @@ import 'nexabase-report/style.css';
|
|
|
64
69
|
<!DOCTYPE html>
|
|
65
70
|
<html>
|
|
66
71
|
<head>
|
|
67
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nexabase-report/dist/style.css">
|
|
72
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/style.css">
|
|
68
73
|
</head>
|
|
69
74
|
<body>
|
|
70
75
|
<nexa-viewer id="viewer" minimal></nexa-viewer>
|
|
71
76
|
|
|
72
|
-
<script src="https://cdn.jsdelivr.net/npm/nexabase-report/dist/nexabase-report.umd.js"></script>
|
|
77
|
+
<script src="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/nexabase-report.umd.js"></script>
|
|
73
78
|
<script>
|
|
74
79
|
NexaReport.registerNexaReport();
|
|
75
80
|
|
|
76
81
|
const viewer = document.getElementById('viewer');
|
|
77
|
-
viewer.definition = { /*
|
|
78
|
-
viewer.data = [ /*
|
|
82
|
+
viewer.definition = { /* definición del reporte */ };
|
|
83
|
+
viewer.data = [ /* datos */ ];
|
|
79
84
|
</script>
|
|
80
85
|
</body>
|
|
81
86
|
</html>
|
|
82
87
|
```
|
|
83
88
|
|
|
89
|
+
Ver ejemplo completo: [`examples/../viewer.html`](examples/../viewer.html)
|
|
90
|
+
|
|
84
91
|
### React
|
|
85
92
|
|
|
86
93
|
```tsx
|
|
@@ -90,8 +97,8 @@ import 'nexabase-report/style.css';
|
|
|
90
97
|
|
|
91
98
|
registerNexaReport();
|
|
92
99
|
|
|
93
|
-
export function ReportViewer({ definition, data }) {
|
|
94
|
-
const ref = useRef<
|
|
100
|
+
export function ReportViewer({ definition, data }: { definition: any; data: any[] }) {
|
|
101
|
+
const ref = useRef<any>(null);
|
|
95
102
|
|
|
96
103
|
useEffect(() => {
|
|
97
104
|
if (!ref.current) return;
|
|
@@ -106,14 +113,14 @@ export function ReportViewer({ definition, data }) {
|
|
|
106
113
|
### Vue 3
|
|
107
114
|
|
|
108
115
|
```vue
|
|
109
|
-
<script setup>
|
|
116
|
+
<script setup lang="ts">
|
|
110
117
|
import { registerNexaReport } from 'nexabase-report';
|
|
111
118
|
import 'nexabase-report/style.css';
|
|
112
119
|
|
|
113
120
|
registerNexaReport();
|
|
114
121
|
|
|
115
122
|
const props = defineProps<{
|
|
116
|
-
definition:
|
|
123
|
+
definition: any;
|
|
117
124
|
data: any[];
|
|
118
125
|
}>();
|
|
119
126
|
</script>
|
|
@@ -136,112 +143,110 @@ import 'nexabase-report/style.css';
|
|
|
136
143
|
|
|
137
144
|
registerNexaReport();
|
|
138
145
|
|
|
139
|
-
// En tu componente
|
|
140
146
|
@Component({
|
|
141
147
|
template: '<nexa-viewer #viewer [definition]="reportDef" [data]="reportData"></nexa-viewer>',
|
|
142
148
|
})
|
|
143
149
|
export class ReportComponent {
|
|
144
150
|
@ViewChild('viewer') viewerRef!: ElementRef;
|
|
145
|
-
|
|
146
|
-
reportDef = null;
|
|
151
|
+
reportDef: any = null;
|
|
147
152
|
reportData: any[] = [];
|
|
148
153
|
|
|
149
154
|
async ngOnInit() {
|
|
150
|
-
|
|
151
|
-
this.reportDef = await res.json();
|
|
155
|
+
this.reportDef = await fetch('/assets/report.json').then(r => r.json());
|
|
152
156
|
this.reportData = await fetch('/api/data').then(r => r.json());
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
159
|
```
|
|
156
160
|
|
|
157
|
-
### Blazor
|
|
161
|
+
### Blazor (.NET 8+)
|
|
158
162
|
|
|
159
163
|
```razor
|
|
160
164
|
@page "/reporte/{Id:int}"
|
|
161
|
-
@inject HttpClient Http
|
|
162
165
|
@inject IJSRuntime JS
|
|
163
166
|
|
|
164
167
|
<link rel="stylesheet" href="_content/nexabase-report/style.css" />
|
|
165
168
|
<script src="_content/nexabase-report/nexabase-report.umd.js"></script>
|
|
166
169
|
|
|
167
|
-
<
|
|
168
|
-
<nexa-viewer id="viewer" minimal></nexa-viewer>
|
|
169
|
-
</div>
|
|
170
|
+
<nexa-viewer id="viewer" minimal></nexa-viewer>
|
|
170
171
|
|
|
171
172
|
@code {
|
|
172
173
|
[Parameter] public int Id { get; set; }
|
|
173
174
|
|
|
174
|
-
protected override async Task
|
|
175
|
+
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
175
176
|
{
|
|
176
|
-
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
await JS.InvokeVoidAsync("renderNexaReport",
|
|
177
|
+
if (!firstRender) return;
|
|
178
|
+
var def = await Http.GetFromJsonAsync<object>($"/api/reportes/{Id}");
|
|
179
|
+
var data = await Http.GetFromJsonAsync<List<object>>($"/api/reportes/{Id}/datos");
|
|
180
|
+
await JS.InvokeVoidAsync("renderNexaReport", def, data);
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
window.nexaRegistered = true;
|
|
189
|
-
}
|
|
190
|
-
const v = document.getElementById('viewer');
|
|
191
|
-
v.definition = definition;
|
|
192
|
-
v.data = data;
|
|
184
|
+
<script>
|
|
185
|
+
window.renderNexaReport = function(definition, data) {
|
|
186
|
+
if (!window.__nexaRegistered) {
|
|
187
|
+
NexaReport.registerNexaReport();
|
|
188
|
+
window.__nexaRegistered = true;
|
|
193
189
|
}
|
|
194
|
-
|
|
195
|
-
|
|
190
|
+
const v = document.getElementById('viewer');
|
|
191
|
+
v.definition = definition;
|
|
192
|
+
v.data = data;
|
|
193
|
+
};
|
|
194
|
+
</script>
|
|
196
195
|
```
|
|
197
196
|
|
|
198
|
-
|
|
197
|
+
---
|
|
199
198
|
|
|
200
|
-
|
|
201
|
-
@* Shared/ReportViewer.razor *@
|
|
202
|
-
@inject IJSRuntime JS
|
|
199
|
+
## Diseñador (NexaDesigner)
|
|
203
200
|
|
|
204
|
-
|
|
205
|
-
<nexa-viewer @ref="viewerRef" id="@Id" minimal></nexa-viewer>
|
|
206
|
-
</div>
|
|
201
|
+
El diseñador WYSIWYG es un componente Vue 3 que se integra en apps con Vue. Incluye:
|
|
207
202
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
203
|
+
- **Ribbon** con formato de texto (fuente, tamaño, color, negrita, cursiva)
|
|
204
|
+
- **Lienzo** con snap-to-grid, rulers y selección múltiple
|
|
205
|
+
- **Panel de propiedades** modular (estilo, datos, condicional)
|
|
206
|
+
- **Diccionario de datos** con campos del datasource
|
|
207
|
+
- **Multilingüe**: español, inglés y portugués (cambiable desde toolbar)
|
|
208
|
+
- **Import/Export** de definiciones JSON
|
|
209
|
+
- **Deshacer/Rehacer** (Ctrl+Z / Ctrl+Y)
|
|
210
|
+
- **Clipboard** (copiar/pegar elementos entre bandas)
|
|
213
211
|
|
|
214
|
-
|
|
212
|
+
### Uso
|
|
215
213
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
}
|
|
214
|
+
```vue
|
|
215
|
+
<script setup lang="ts">
|
|
216
|
+
import { NexaDesigner } from 'nexabase-report';
|
|
217
|
+
import 'nexabase-report/style.css';
|
|
218
|
+
import { ref } from 'vue';
|
|
223
219
|
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
const designerRef = ref(null);
|
|
221
|
+
const locale = ref('es'); // 'es' | 'en' | 'pt'
|
|
222
|
+
|
|
223
|
+
function onSave(reportDef: any) {
|
|
224
|
+
console.log('Reporte guardado:', reportDef);
|
|
226
225
|
}
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<template>
|
|
229
|
+
<NexaDesigner
|
|
230
|
+
ref="designerRef"
|
|
231
|
+
:locale="locale"
|
|
232
|
+
@save="onSave"
|
|
233
|
+
style="height: 100vh"
|
|
234
|
+
/>
|
|
235
|
+
</template>
|
|
227
236
|
```
|
|
228
237
|
|
|
229
|
-
|
|
238
|
+
### Props
|
|
230
239
|
|
|
231
|
-
|
|
240
|
+
| Prop | Tipo | Default | Descripción |
|
|
241
|
+
|------|------|---------|-------------|
|
|
242
|
+
| `locale` | `'es' \| 'en' \| 'pt'` | `'es'` | Idioma del diseñador |
|
|
243
|
+
| `initialReport` | `NexaReportDefinition` | — | Reporte a editar (nuevo si no se pasa) |
|
|
232
244
|
|
|
233
|
-
|
|
234
|
-
|---------|-------------|------------|
|
|
235
|
-
| **Tag** | `<NexaReportDesigner>` | `<nexa-viewer>` |
|
|
236
|
-
| **Framework** | Requiere Vue 3 | Framework-agnostic |
|
|
237
|
-
| **Uso** | Crear/editar reportes | Solo visualización |
|
|
238
|
-
| **Exportación** | No | Sí (PDF, Excel, Word, CSV) |
|
|
239
|
-
| **Props data** | No | Sí |
|
|
245
|
+
### Eventos
|
|
240
246
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
3. Usuario exporta / imprime desde el viewer
|
|
247
|
+
| Evento | Payload | Descripción |
|
|
248
|
+
|--------|---------|-------------|
|
|
249
|
+
| `save` | `NexaReportDefinition` | Usuario presionó guardar |
|
|
245
250
|
|
|
246
251
|
---
|
|
247
252
|
|
|
@@ -251,15 +256,15 @@ O si prefieres usar un componente wrapper en Razor:
|
|
|
251
256
|
|
|
252
257
|
| Prop | Tipo | Default | Descripción |
|
|
253
258
|
|------|------|---------|-------------|
|
|
254
|
-
| `definition` | `string \|
|
|
255
|
-
| `data` | `string \| any[] \|
|
|
256
|
-
| `parameters` | `
|
|
259
|
+
| `definition` | `string \| NexaReportDefinition` | — | Definición del reporte (objeto o JSON string) |
|
|
260
|
+
| `data` | `string \| any[] \| Record<string, any[]>` | — | Datos: array simple, objeto multi-alias, o JSON string |
|
|
261
|
+
| `parameters` | `Record<string, any>` | — | Valores iniciales para los parámetros |
|
|
257
262
|
| `minimal` | `boolean` | `false` | Oculta toolbar y thumbnails |
|
|
258
|
-
| `showToolbar` | `boolean` | `true` | Muestra
|
|
259
|
-
| `showThumbs` | `boolean` | `true` | Muestra
|
|
260
|
-
| `skipParamsDialog` | `boolean` | `false` | Salta el diálogo de parámetros |
|
|
263
|
+
| `showToolbar` | `boolean` | `true` | Muestra barra superior de exportación |
|
|
264
|
+
| `showThumbs` | `boolean` | `true` | Muestra miniaturas laterales |
|
|
265
|
+
| `skipParamsDialog` | `boolean` | `false` | Salta el diálogo de parámetros al cargar |
|
|
261
266
|
| `currentPage` | `number` | `1` | Página inicial |
|
|
262
|
-
| `apiBaseUrl` | `string` | — | URL base de NexaBase |
|
|
267
|
+
| `apiBaseUrl` | `string` | — | URL base de NexaBase (backend) |
|
|
263
268
|
| `apiKey` | `string` | — | API key para NexaBase |
|
|
264
269
|
|
|
265
270
|
### Métodos (DOM)
|
|
@@ -267,13 +272,16 @@ O si prefieres usar un componente wrapper en Razor:
|
|
|
267
272
|
```ts
|
|
268
273
|
const v = document.querySelector('nexa-viewer');
|
|
269
274
|
|
|
270
|
-
await v.exportPdf(); // PDF vectorial
|
|
271
|
-
await v.exportPdfWithBookmarks(); // PDF
|
|
275
|
+
await v.exportPdf(); // PDF vectorial (jsPDF)
|
|
276
|
+
await v.exportPdfWithBookmarks(); // PDF + panel de marcadores
|
|
272
277
|
await v.exportExcel(); // .xlsx (SheetJS)
|
|
273
278
|
await v.exportWord(); // .docx
|
|
274
|
-
await v.exportCsv(); // .csv
|
|
279
|
+
await v.exportCsv(); // .csv (UTF-8 BOM)
|
|
280
|
+
|
|
275
281
|
v.goToPage(3); // Navegar a página
|
|
276
|
-
v.updateData(nuevosDatos); // Actualizar datos
|
|
282
|
+
v.updateData(nuevosDatos); // Actualizar datos sin recargar definición
|
|
283
|
+
|
|
284
|
+
console.log(v.pageNumber, v.totalPages, v.paramValues);
|
|
277
285
|
```
|
|
278
286
|
|
|
279
287
|
### Eventos
|
|
@@ -281,13 +289,15 @@ v.updateData(nuevosDatos); // Actualizar datos
|
|
|
281
289
|
```ts
|
|
282
290
|
v.addEventListener('page-change', e => console.log(e.detail.page));
|
|
283
291
|
v.addEventListener('drill-click', e => console.log(e.detail));
|
|
292
|
+
v.addEventListener('subreport-toggle', e => console.log(e.detail));
|
|
293
|
+
v.addEventListener('data-request', e => console.log(e.detail.alias));
|
|
284
294
|
```
|
|
285
295
|
|
|
286
296
|
---
|
|
287
297
|
|
|
288
298
|
## Formato de datos
|
|
289
299
|
|
|
290
|
-
###
|
|
300
|
+
### Array simple (único DataSource)
|
|
291
301
|
|
|
292
302
|
```json
|
|
293
303
|
[
|
|
@@ -296,9 +306,7 @@ v.addEventListener('drill-click', e => console.log(e.detail));
|
|
|
296
306
|
]
|
|
297
307
|
```
|
|
298
308
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
### Multiple DataSources (objeto con alias)
|
|
309
|
+
### Múltiples DataSources (objeto con alias)
|
|
302
310
|
|
|
303
311
|
```json
|
|
304
312
|
{
|
|
@@ -306,21 +314,21 @@ El reporte usa `dataSource: ""` (vacío) para tomar este array como `"main"`.
|
|
|
306
314
|
{ "id": 1, "nombre": "Juan", "ciudad": "Bogotá" }
|
|
307
315
|
],
|
|
308
316
|
"pedidos": [
|
|
309
|
-
{ "cliente_id": 1, "producto": "Camisa", "cantidad": 3 }
|
|
317
|
+
{ "cliente_id": 1, "producto": "Camisa", "cantidad": 3 },
|
|
318
|
+
{ "cliente_id": 1, "producto": "Pantalón", "cantidad": 2 }
|
|
310
319
|
]
|
|
311
320
|
}
|
|
312
321
|
```
|
|
313
322
|
|
|
314
|
-
El reporte usa `dataSource: "clientes"`
|
|
323
|
+
El reporte usa `dataSource: "clientes"` (o el alias correspondiente) en la banda.
|
|
315
324
|
|
|
316
|
-
###
|
|
325
|
+
### Parámetros
|
|
317
326
|
|
|
318
327
|
```ts
|
|
319
|
-
// Pasando parameters al viewer
|
|
320
328
|
viewer.parameters = {
|
|
321
329
|
fechaInicio: '2024-01-01',
|
|
322
330
|
fechaFin: '2024-12-31',
|
|
323
|
-
categoria: '
|
|
331
|
+
categoria: 'Electronics'
|
|
324
332
|
};
|
|
325
333
|
```
|
|
326
334
|
|
|
@@ -334,95 +342,88 @@ viewer.parameters = {
|
|
|
334
342
|
|----------|-------------|---------|
|
|
335
343
|
| `{{Page}}` | Página actual | `1` |
|
|
336
344
|
| `{{TotalPages}}` | Total de páginas | `5` |
|
|
337
|
-
| `{{Today}}` | Fecha actual (YYYY-MM-DD) | `
|
|
338
|
-
| `{{Now}}` | Fecha y hora actual | `
|
|
339
|
-
| `{{Year}}` | Año actual | `
|
|
340
|
-
| `{{Month}}` | Mes actual | `
|
|
341
|
-
| `{{Day}}` | Día actual | `
|
|
345
|
+
| `{{Today}}` | Fecha actual (YYYY-MM-DD) | `2026-05-13` |
|
|
346
|
+
| `{{Now}}` | Fecha y hora actual | `2026-05-13 14:30:00` |
|
|
347
|
+
| `{{Year}}` | Año actual | `2026` |
|
|
348
|
+
| `{{Month}}` | Mes actual | `5` |
|
|
349
|
+
| `{{Day}}` | Día actual | `13` |
|
|
342
350
|
| `{{ReportName}}` | Nombre del reporte | `Ventas Mensuales` |
|
|
343
351
|
| `{{RowNumber}}` | Número de fila (1-based) | `1` |
|
|
344
352
|
| `{{TotalRows}}` | Total de filas | `50` |
|
|
345
|
-
| `{{EvenRow}}`
|
|
346
|
-
| `{{
|
|
347
|
-
| `{{
|
|
348
|
-
| `{{
|
|
349
|
-
|
|
353
|
+
| `{{EvenRow}}` / `{{OddRow}}` | Fila par / impar | `true` / `false` |
|
|
354
|
+
| `{{FirstRow}}` / `{{LastRow}}` | Primera / última fila | `true` |
|
|
355
|
+
| `{{GroupKey}}` | Clave del grupo actual | `Electronics` |
|
|
356
|
+
| `{{GroupCount}}` | Registros en el grupo actual | `12` |
|
|
357
|
+
|
|
358
|
+
### Expresiones `{[...]}`
|
|
359
|
+
|
|
360
|
+
| Expresión | Resultado |
|
|
361
|
+
|-----------|-----------|
|
|
362
|
+
| `{[FormatNumber(precio, 'es-ES')]}` | `1.234,56` |
|
|
363
|
+
| `{[FormatDate(fecha, 'dd/MM/yyyy')]}` | `15/01/2024` |
|
|
364
|
+
| `{[FormatCurrency(total, 'USD')]}` | `$1,234.56` |
|
|
365
|
+
| `{[IIF(total > 100000, 'ALTO', 'BAJO')]}` | `ALTO` |
|
|
366
|
+
| `{[ISNULL(cliente, 'Sin nombre')]}` | `Sin nombre` |
|
|
367
|
+
| `{[UPPER(nombre)]}` | `JUAN PÉREZ` |
|
|
368
|
+
| `{[SUBSTRING(texto, 0, 3)]}` | `Hel` |
|
|
369
|
+
| `{[ABS(-5)]}` / `{[CEIL(5.3)]}` / `{[FLOOR(5.9)]}` | `5` / `6` / `5` |
|
|
370
|
+
| `{[SUM(cantidad)]}` / `{[COUNT(id)]}` / `{[AVG(precio)]}` | Agregaciones |
|
|
371
|
+
| `{[CONCAT(nombre, ' - ', ciudad)]}` | `Juan - Bogotá` |
|
|
350
372
|
|
|
351
|
-
|
|
373
|
+
---
|
|
352
374
|
|
|
353
|
-
|
|
354
|
-
{[FormatNumber(precio, 'es-ES')]} → 1.234,56
|
|
355
|
-
{[FormatDate(fecha, 'dd/MM/yyyy')]} → 15/01/2024
|
|
356
|
-
{[FormatCurrency(total, 'USD')]} → $1,234.56
|
|
357
|
-
{[IIF(total > 100000, 'ALTO', 'BAJO')]} → ALTO
|
|
358
|
-
{[ISNULL(cliente, 'Sin nombre')]} → Sin nombre
|
|
359
|
-
{[UPPER(nombre)]} → JUAN PÉREZ
|
|
360
|
-
{[SUBSTRING(texto, 0, 3)]} → "Hel" (primeros 3)
|
|
361
|
-
{[ABS(-5)]} → 5
|
|
362
|
-
{[CEIL(5.3)]} → 6
|
|
363
|
-
{[FLOOR(5.9)]} → 5
|
|
364
|
-
```
|
|
375
|
+
## Ejemplos
|
|
365
376
|
|
|
366
|
-
|
|
377
|
+
Los reportes de ejemplo están en `examples/` y son compatibles tanto con el diseñador como con el viewer.
|
|
367
378
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
379
|
+
| Archivo | Descripción |
|
|
380
|
+
|---------|-------------|
|
|
381
|
+
| `report-factura.json` | Factura con ítems, cliente y totales |
|
|
382
|
+
| `factura_de_recolección_de_residuos.json` | Factura ambiental con múltiples DataSources |
|
|
383
|
+
| `report-factura-residuos.json` | Factura de residuos con GroupHeader/Footer |
|
|
384
|
+
| `report-anexo-factura.json` | Anexo complementario con tabla expandida |
|
|
385
|
+
| `report-ventas-logo-tabla.json` | Reporte corporativo con logo y tabla agrupada |
|
|
386
|
+
| `report-agrupado-por-cliente.json` | GroupHeader + Table con agrupación por cliente |
|
|
387
|
+
| `report-productos.json` | Lista simple de productos |
|
|
388
|
+
| `report-crosstab-categoria-mes.json` | Tabla dinámica (crosstab) categoría × mes |
|
|
389
|
+
| `report-grafico-ventas-por-mes.json` | Gráfico de barras (ECharts) |
|
|
390
|
+
| `report-master-detail-ordenes.json` | Master-Detail con subreportes relacionados |
|
|
391
|
+
| `report-codigos-qr-barcode.json` | Códigos QR y barras desde datos |
|
|
380
392
|
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
"content": "Cliente: {{nombre}} ({{ciudad}})",
|
|
385
|
-
"binding": "nombre"
|
|
386
|
-
}
|
|
393
|
+
```bash
|
|
394
|
+
# Probar un ejemplo local
|
|
395
|
+
curl -s https://raw.githubusercontent.com/nexabase/nexabase-report/main/examples/report-factura.json
|
|
387
396
|
```
|
|
388
397
|
|
|
389
398
|
---
|
|
390
399
|
|
|
391
|
-
##
|
|
392
|
-
|
|
393
|
-
Los ejemplos JSON en `examples/` son reportes completos que puedes cargar:
|
|
400
|
+
## Publicación
|
|
394
401
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
| `report-agrupado-por-cliente.json` | GroupHeader + Table con agrupación |
|
|
401
|
-
| `report-productos.json` | Lista simple de productos |
|
|
402
|
-
| `report-crosstab-categoria-mes.json` | Tabla dinámica (crosstab) |
|
|
403
|
-
| `report-grafico-ventas-por-mes.json` | Gráfico de barras con datos mensuales |
|
|
404
|
-
| `report-master-detail-ordenes.json` | Master-detail con subreportes |
|
|
405
|
-
| `report-codigos-qr-barcode.json` | Códigos QR y barras |
|
|
406
|
-
|
|
407
|
-
### Cargar un ejemplo
|
|
402
|
+
```bash
|
|
403
|
+
npm run build
|
|
404
|
+
npm version patch|minor|major
|
|
405
|
+
npm publish --access public
|
|
406
|
+
```
|
|
408
407
|
|
|
409
|
-
|
|
410
|
-
const res = await fetch('/examples/report-factura.json');
|
|
411
|
-
const definition = await res.json();
|
|
412
|
-
const data = [ /* tus datos */ ];
|
|
408
|
+
Disponible en CDN automáticamente tras publicar:
|
|
413
409
|
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
```html
|
|
411
|
+
<script src="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/nexabase-report.umd.js"></script>
|
|
412
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/style.css">
|
|
416
413
|
```
|
|
417
414
|
|
|
415
|
+
Ver [`docs/PUBLISHING.md`](docs/PUBLISHING.md) para CI/CD y registry privado.
|
|
416
|
+
|
|
418
417
|
---
|
|
419
418
|
|
|
420
419
|
## Documentación adicional
|
|
421
420
|
|
|
422
|
-
- [API del Viewer](docs/VIEWER_API.md) —
|
|
423
|
-
- [
|
|
424
|
-
- [
|
|
421
|
+
- [API completa del Viewer](docs/VIEWER_API.md) — Props, métodos, eventos, tipos
|
|
422
|
+
- [API REST (funciones serverless)](docs/API_REST.md) — Endpoints para listar/render/exportar
|
|
423
|
+
- [Integración externa](docs/EXTERNAL_INTEGRATION.md) — ASP.NET, Django, Laravel, Rails
|
|
425
424
|
- [Plan de QA](docs/QA_PLAN.md) — Checklist de pruebas manuales
|
|
425
|
+
- [Checklist de regresión exportación](docs/EXPORT_REGRESSION.md) — PDF, Excel, Word, CSV
|
|
426
|
+
- [Guía de publicación npm](docs/PUBLISHING.md) — Build, versionado, CI/CD
|
|
426
427
|
|
|
427
428
|
---
|
|
428
429
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as c } from "./index-
|
|
2
|
-
import { r as f } from "./html2canvas-
|
|
1
|
+
import { a as c } from "./index-CrlafZ0y.js";
|
|
2
|
+
import { r as f } from "./html2canvas-Bz1P596e.js";
|
|
3
3
|
function l(r, n) {
|
|
4
4
|
for (var o = 0; o < n.length; o++) {
|
|
5
5
|
const e = n[o];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { g as De, a as Ke, c as Te } from "./index-
|
|
2
|
-
import { j as Ge } from "./jspdf.es.min-
|
|
3
|
-
import { r as Be } from "./html2canvas-
|
|
1
|
+
import { g as De, a as Ke, c as Te } from "./index-CrlafZ0y.js";
|
|
2
|
+
import { j as Ge } from "./jspdf.es.min-DEtCEkBC.js";
|
|
3
|
+
import { r as Be } from "./html2canvas-Bz1P596e.js";
|
|
4
4
|
function Ue(ge, we) {
|
|
5
5
|
for (var me = 0; me < we.length; me++) {
|
|
6
6
|
const ce = we[me];
|