nexabase-report 0.2.14 → 0.3.2
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 +250 -100
- package/dist/{html2canvas-CO3zd_po.js → html2canvas-AtLyXWtP.js} +2 -2
- package/dist/{html2canvas-Bz1P596e.js → html2canvas-B37RfnAd.js} +1 -1
- package/dist/{html2pdf-BKFAbMM9.js → html2pdf-MO-8nuYw.js} +3 -3
- package/dist/{index-CrlafZ0y.js → index-DLc-dsvr.js} +14596 -14537
- package/dist/{index.es-Dn-NWvkn.js → index.es-CEDOM-ff.js} +2 -2
- package/dist/{jspdf.es.min-DEtCEkBC.js → jspdf.es.min-P9H2mKOY.js} +2 -2
- package/dist/nexabase-report.es.js +1 -1
- package/dist/nexabase-report.umd.js +141 -141
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,22 +5,7 @@
|
|
|
5
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**
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Tabla de contenidos
|
|
13
|
-
|
|
14
|
-
- [Características](#características)
|
|
15
|
-
- [Instalación](#instalación)
|
|
16
|
-
- [Uso rápido](#uso-rápido)
|
|
17
|
-
- [Diseñador (NexaDesigner)](#diseñador-nexadesigner)
|
|
18
|
-
- [API del Viewer](#api-del-viewer)
|
|
19
|
-
- [Formato de datos](#formato-de-datos)
|
|
20
|
-
- [Variables y expresiones](#variables-y-expresiones)
|
|
21
|
-
- [Ejemplos](#ejemplos)
|
|
22
|
-
- [Documentación adicional](#documentación-adicional)
|
|
23
|
-
- [Publicación](#publicación)
|
|
8
|
+
> Librería Vue 3 + TypeScript para diseñar y visualizar reportes tipo banded (inspirada en Stimulsoft / DevExpress). Incluye **diseñador WYSIWYG** multilingüe (ES/EN/PT), **visor como Custom Element** framework-agnostic, y **exportación 100% cliente** a PDF, Excel, Word y CSV.
|
|
24
9
|
|
|
25
10
|
---
|
|
26
11
|
|
|
@@ -29,21 +14,27 @@
|
|
|
29
14
|
| Característica | Descripción |
|
|
30
15
|
|----------------|-------------|
|
|
31
16
|
| **Custom Element** | `<nexa-viewer>` funciona en Vue, React, Angular, Svelte, Blazor o HTML puro |
|
|
32
|
-
| **Diseñador WYSIWYG** | `NexaDesigner.vue` —
|
|
33
|
-
| **Visor standalone** |
|
|
34
|
-
| **Exportación 100% cliente** | PDF
|
|
35
|
-
| **
|
|
36
|
-
| **
|
|
17
|
+
| **Diseñador WYSIWYG** | `NexaDesigner.vue` — ribbon, lienzo con snap-to-grid, sidebars, i18n |
|
|
18
|
+
| **Visor standalone** | Sin dependencia de Vue en el consumidor |
|
|
19
|
+
| **Exportación 100% cliente** | PDF (html2pdf.js + fallback jsPDF vectorial), Excel (SheetJS), Word (docx), CSV (UTF-8 BOM) |
|
|
20
|
+
| **Motor de paginación** | Algoritmo en dos fases: streaming + particionado por página, con splitting de tablas a través de páginas |
|
|
21
|
+
| **Renderers** | Texto, imágenes, barras, QR, shapes, drill-down, charts (ECharts), crosstabs, subreportes, widgets |
|
|
22
|
+
| **Watermarks** | Modo tile (texto repetido en grilla) o single posicionado (center, top-left, bottom-right, etc.) |
|
|
23
|
+
| **TOC** | Tabla de contenidos auto-generada en página 1 con enlaces a secciones |
|
|
24
|
+
| **Dashboard** | Modo de visualización con grilla de widgets |
|
|
25
|
+
| **Gráficos** | ECharts (barras, líneas, pastel, área, scatter, radar, etc.) |
|
|
26
|
+
| **Tablas dinámicas** | Crosstabs con agregaciones (sum, count, avg, min, max) |
|
|
37
27
|
| **Códigos QR / Barras** | jsbarcode + qrcode con bindings a datos |
|
|
38
|
-
| **Formato condicional** | Reglas
|
|
39
|
-
| **Master-Detail** |
|
|
40
|
-
| **Drill-Down** | Navegación a
|
|
41
|
-
| **Subreportes** | Anidamiento
|
|
42
|
-
| **Motor de expresiones** | Seguro (sin `eval`): `FormatNumber`, `FormatDate`, `IIF`, `ISNULL`, agregaciones |
|
|
43
|
-
| **Parámetros** | Diálogo de entrada,
|
|
44
|
-
| **
|
|
45
|
-
| **
|
|
46
|
-
| **
|
|
28
|
+
| **Formato condicional** | Reglas por operador (eq, gt, contains, between, regex, etc.) |
|
|
29
|
+
| **Master-Detail** | DataBand con DetailBand hijo, filtro por campo clave |
|
|
30
|
+
| **Drill-Down** | Navegación a reporte destino con paso de parámetros |
|
|
31
|
+
| **Subreportes** | Anidamiento vía definición embebida o remota (API REST) |
|
|
32
|
+
| **Motor de expresiones** | Seguro (sin `eval`): `FormatNumber`, `FormatDate`, `IIF`, `ISNULL`, agregaciones, `SUBSTRING`, etc. |
|
|
33
|
+
| **Parámetros** | Diálogo de entrada con tipos (text, number, date, boolean, select), validación, skip opcional |
|
|
34
|
+
| **Shapes** | Rectángulos, elipses, líneas y flechas |
|
|
35
|
+
| **Búsqueda** | Highlight inline con navegación entre resultados |
|
|
36
|
+
| **Zoom** | Zoom +/- 10%, fitToWidth, fitToPage, modo continuo/página simple |
|
|
37
|
+
| **i18n** | Visor y diseñador en español, inglés, portugués |
|
|
47
38
|
|
|
48
39
|
---
|
|
49
40
|
|
|
@@ -53,7 +44,7 @@
|
|
|
53
44
|
npm install nexabase-report
|
|
54
45
|
```
|
|
55
46
|
|
|
56
|
-
Importar estilos
|
|
47
|
+
Importar estilos una sola vez al inicio:
|
|
57
48
|
|
|
58
49
|
```ts
|
|
59
50
|
import 'nexabase-report/style.css';
|
|
@@ -86,8 +77,6 @@ import 'nexabase-report/style.css';
|
|
|
86
77
|
</html>
|
|
87
78
|
```
|
|
88
79
|
|
|
89
|
-
Ver ejemplo completo: [`examples/../viewer.html`](examples/../viewer.html)
|
|
90
|
-
|
|
91
80
|
### React
|
|
92
81
|
|
|
93
82
|
```tsx
|
|
@@ -118,11 +107,6 @@ import { registerNexaReport } from 'nexabase-report';
|
|
|
118
107
|
import 'nexabase-report/style.css';
|
|
119
108
|
|
|
120
109
|
registerNexaReport();
|
|
121
|
-
|
|
122
|
-
const props = defineProps<{
|
|
123
|
-
definition: any;
|
|
124
|
-
data: any[];
|
|
125
|
-
}>();
|
|
126
110
|
</script>
|
|
127
111
|
|
|
128
112
|
<template>
|
|
@@ -198,16 +182,16 @@ export class ReportComponent {
|
|
|
198
182
|
|
|
199
183
|
## Diseñador (NexaDesigner)
|
|
200
184
|
|
|
201
|
-
|
|
185
|
+
Diseñador WYSIWYG como componente Vue 3:
|
|
202
186
|
|
|
203
|
-
- **Ribbon**
|
|
204
|
-
- **Lienzo**
|
|
205
|
-
- **Panel de propiedades**
|
|
206
|
-
- **Diccionario de datos**
|
|
207
|
-
- **Multilingüe
|
|
208
|
-
- **Import/Export**
|
|
209
|
-
- **Deshacer/Rehacer**
|
|
210
|
-
- **Clipboard**
|
|
187
|
+
- **Ribbon** — formato de texto (fuente, tamaño, color, negrita, cursiva, alineación)
|
|
188
|
+
- **Lienzo** — snap-to-grid (5 px), rulers, selección múltiple, arrastrar/redimensionar
|
|
189
|
+
- **Panel de propiedades** — secciones: apariencia, datos, bordes, formato condicional
|
|
190
|
+
- **Diccionario de datos** — campos del datasource con drag al lienzo
|
|
191
|
+
- **Multilingüe** — español, inglés, portugués, cambiable desde la barra
|
|
192
|
+
- **Import/Export** — definiciones JSON
|
|
193
|
+
- **Deshacer/Rehacer** — Ctrl+Z / Ctrl+Y
|
|
194
|
+
- **Clipboard** — copiar/pegar elementos entre bandas
|
|
211
195
|
|
|
212
196
|
### Uso
|
|
213
197
|
|
|
@@ -218,7 +202,7 @@ import 'nexabase-report/style.css';
|
|
|
218
202
|
import { ref } from 'vue';
|
|
219
203
|
|
|
220
204
|
const designerRef = ref(null);
|
|
221
|
-
const locale = ref('es');
|
|
205
|
+
const locale = ref('es');
|
|
222
206
|
|
|
223
207
|
function onSave(reportDef: any) {
|
|
224
208
|
console.log('Reporte guardado:', reportDef);
|
|
@@ -226,12 +210,7 @@ function onSave(reportDef: any) {
|
|
|
226
210
|
</script>
|
|
227
211
|
|
|
228
212
|
<template>
|
|
229
|
-
<NexaDesigner
|
|
230
|
-
ref="designerRef"
|
|
231
|
-
:locale="locale"
|
|
232
|
-
@save="onSave"
|
|
233
|
-
style="height: 100vh"
|
|
234
|
-
/>
|
|
213
|
+
<NexaDesigner ref="designerRef" :locale="locale" @save="onSave" style="height: 100vh" />
|
|
235
214
|
</template>
|
|
236
215
|
```
|
|
237
216
|
|
|
@@ -250,54 +229,219 @@ function onSave(reportDef: any) {
|
|
|
250
229
|
|
|
251
230
|
---
|
|
252
231
|
|
|
253
|
-
##
|
|
232
|
+
## Visor (`<nexa-viewer>`)
|
|
233
|
+
|
|
234
|
+
El visor se registra como Custom Element y funciona sin Vue en el proyecto consumidor.
|
|
235
|
+
|
|
236
|
+
### Formas de uso
|
|
237
|
+
|
|
238
|
+
```html
|
|
239
|
+
<nexa-viewer
|
|
240
|
+
minimal
|
|
241
|
+
showToolbar
|
|
242
|
+
showThumbs
|
|
243
|
+
locale="es"
|
|
244
|
+
currentPage="1"
|
|
245
|
+
skipParamsDialog
|
|
246
|
+
></nexa-viewer>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Como Vue SFC directamente:
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<script setup lang="ts">
|
|
253
|
+
import { NexaViewerElement, registerNexaReport } from 'nexabase-report';
|
|
254
|
+
import 'nexabase-report/style.css';
|
|
255
|
+
|
|
256
|
+
registerNexaReport();
|
|
257
|
+
</script>
|
|
258
|
+
|
|
259
|
+
<template>
|
|
260
|
+
<nexa-viewer
|
|
261
|
+
:definition="reportDef"
|
|
262
|
+
:data="reportData"
|
|
263
|
+
:parameters="params"
|
|
264
|
+
locale="es"
|
|
265
|
+
@page-change="onPageChange"
|
|
266
|
+
@drill-click="onDrillClick"
|
|
267
|
+
/>
|
|
268
|
+
</template>
|
|
269
|
+
```
|
|
254
270
|
|
|
255
271
|
### Props
|
|
256
272
|
|
|
257
273
|
| Prop | Tipo | Default | Descripción |
|
|
258
274
|
|------|------|---------|-------------|
|
|
259
275
|
| `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
|
|
276
|
+
| `data` | `string \| any[] \| Record<string, any[]>` | — | Datos: array simple (alias "main"), objeto multi-alias, o JSON string |
|
|
277
|
+
| `parameters` | `Record<string, any>` | — | Valores iniciales de parámetros |
|
|
262
278
|
| `minimal` | `boolean` | `false` | Oculta toolbar y thumbnails |
|
|
263
|
-
| `showToolbar` | `boolean` | `true` |
|
|
264
|
-
| `showThumbs` | `boolean` | `true` |
|
|
279
|
+
| `showToolbar` | `boolean` | `true` | Forzar toolbar visible (útil en minimal) |
|
|
280
|
+
| `showThumbs` | `boolean` | `true` | Forzar miniaturas visibles (útil en minimal) |
|
|
265
281
|
| `skipParamsDialog` | `boolean` | `false` | Salta el diálogo de parámetros al cargar |
|
|
266
|
-
| `currentPage` | `number` | `1` | Página inicial |
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
282
|
+
| `currentPage` | `number` | `1` | Página inicial (soporta v-model) |
|
|
283
|
+
| `locale` | `string` | `'es'` | Idioma: `'es'`, `'en'`, `'pt'` |
|
|
284
|
+
| `apiBaseUrl` | `string` | — | URL base para subreportes/drill-down remotos |
|
|
285
|
+
| `apiKey` | `string` | — | API Key para requests al backend |
|
|
269
286
|
|
|
270
|
-
### Métodos (DOM)
|
|
287
|
+
### Métodos (acceso vía ref o DOM)
|
|
271
288
|
|
|
272
289
|
```ts
|
|
273
290
|
const v = document.querySelector('nexa-viewer');
|
|
274
291
|
|
|
275
|
-
|
|
276
|
-
await v.
|
|
292
|
+
// Exportación
|
|
293
|
+
await v.exportPdf(); // pdf (html2pdf.js — fallback jsPDF vectorial)
|
|
294
|
+
await v.exportPdfWithBookmarks(); // pdf con marcadores
|
|
277
295
|
await v.exportExcel(); // .xlsx (SheetJS)
|
|
278
296
|
await v.exportWord(); // .docx
|
|
279
|
-
await v.exportCsv(); // .csv
|
|
297
|
+
await v.exportCsv(); // .csv con UTF-8 BOM
|
|
298
|
+
|
|
299
|
+
// Navegación
|
|
300
|
+
v.goToPage(5);
|
|
301
|
+
|
|
302
|
+
// Datos
|
|
303
|
+
v.updateData(nuevosDatos);
|
|
304
|
+
|
|
305
|
+
// Parámetros
|
|
306
|
+
v.applyParams();
|
|
307
|
+
v.applyParamsWithValidation();
|
|
308
|
+
v.validateParams(); // retorna errores
|
|
280
309
|
|
|
281
|
-
|
|
282
|
-
v.
|
|
310
|
+
// Utilidad
|
|
311
|
+
v.getDictionaryFields(); // retorna campos disponibles
|
|
283
312
|
|
|
284
|
-
|
|
313
|
+
// Propiedades de lectura
|
|
314
|
+
v.pageNumber; // número actual
|
|
315
|
+
v.totalPages; // total de páginas
|
|
316
|
+
v.paramValues; // valores actuales de parámetros
|
|
317
|
+
v.showParamsDialog; // estado del diálogo
|
|
318
|
+
v.paramValidationErrors; // errores de validación
|
|
285
319
|
```
|
|
286
320
|
|
|
287
321
|
### Eventos
|
|
288
322
|
|
|
289
323
|
```ts
|
|
290
|
-
v.addEventListener('page-change',
|
|
291
|
-
v.addEventListener('drill-click',
|
|
324
|
+
v.addEventListener('page-change', e => console.log(e.detail.page));
|
|
325
|
+
v.addEventListener('drill-click', e => console.log(e.detail));
|
|
292
326
|
v.addEventListener('subreport-toggle', e => console.log(e.detail));
|
|
293
|
-
v.addEventListener('data-request',
|
|
327
|
+
v.addEventListener('data-request', e => console.log(e.detail.alias));
|
|
294
328
|
```
|
|
295
329
|
|
|
296
330
|
---
|
|
297
331
|
|
|
332
|
+
## Arquitectura del visor
|
|
333
|
+
|
|
334
|
+
### Template
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
<nexa-viewer>
|
|
338
|
+
├── Toolbar (condicional)
|
|
339
|
+
│ ├── Exportación: PDF, Excel, Word, CSV, imprimir
|
|
340
|
+
│ ├── Zoom: −, %, +, fitToWidth, fitToPage
|
|
341
|
+
│ └── Paginación: ◀, input/total, ▶, búsqueda, info
|
|
342
|
+
│
|
|
343
|
+
├── Sidebar (condicional, 220px)
|
|
344
|
+
│ ├── Pestaña Pages — miniaturas vía html2canvas
|
|
345
|
+
│ ├── Pestaña TOC — tabla de contenidos con navegación
|
|
346
|
+
│ └── Pestaña Params — formulario de parámetros (text, number, date, boolean, select)
|
|
347
|
+
│
|
|
348
|
+
├── Viewport (scrollable)
|
|
349
|
+
│ └── Pages root (transform: scale(Z%) )
|
|
350
|
+
│ ├── Dashboard: grilla de DashboardWidgetRenderer
|
|
351
|
+
│ └── Reporte: v-for pages
|
|
352
|
+
│ └── report-page (sombra, fondo blanco)
|
|
353
|
+
│ ├── Watermark (tile o single posicionado)
|
|
354
|
+
│ ├── TOC page (si página 1 y generateTOC)
|
|
355
|
+
│ └── Bands
|
|
356
|
+
│ ├── DataBand tabular → Table, Chart, Crosstab, SubReport
|
|
357
|
+
│ └── DataBand iterativa → Text, Image, Barcode, QR, Shape, DrillDown
|
|
358
|
+
│
|
|
359
|
+
├── Modal DrillDown (reporte anidado vía API)
|
|
360
|
+
└── Modal About
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### PaginationEngine
|
|
364
|
+
|
|
365
|
+
El motor de paginación es una clase (`src/lib/viewer/services/PaginationEngine.ts`) que opera en **dos fases**:
|
|
366
|
+
|
|
367
|
+
**Fase 1 — generateStream()**: Generator que produce un stream de `PageBandDef`:
|
|
368
|
+
1. Emite `ReportHeader` primero
|
|
369
|
+
2. Bandas estáticas (ni DataBand ni DetailBand)
|
|
370
|
+
3. Para cada `DataBand`:
|
|
371
|
+
- **Tabular** (Table, Chart, Crosstab, SubReport): emite la banda completa con todas las filas en `rows`
|
|
372
|
+
- **Iterativa** (una instancia por fila): emite una banda por fila, intercalando GroupHeader/GroupFooter según agrupación y procesando DetailBand hijos (master-detail)
|
|
373
|
+
4. Emite `ReportFooter` al final
|
|
374
|
+
|
|
375
|
+
**Fase 2 — generatePages()**: Algoritmo de particionado:
|
|
376
|
+
- Calcula altura disponible: `pageHeight - pageHeader - pageFooter`
|
|
377
|
+
- Itera el stream, acumulando bandas
|
|
378
|
+
- Si una banda **tabular** no cabe:
|
|
379
|
+
- **Tabla**: calcula filas que entran (`(h disponible - h header) / h fila`), corta en chunks, coloca cada chunk en página nueva
|
|
380
|
+
- **Tabla agrupada**: corta por grupos completos (respeta `tableShowGroupHeader`/`tableShowGroupFooter`)
|
|
381
|
+
- Charts/Crosstabs: saltan a página nueva enteros
|
|
382
|
+
- Respeta `pageBreakBefore` / `pageBreakAfter`
|
|
383
|
+
- Asigna coordenadas Y absolutas para posicionamiento CSS
|
|
384
|
+
- Cada ~10 páginas cede el hilo (`setTimeout`) para no bloquear en datasets grandes
|
|
385
|
+
|
|
386
|
+
Métodos estáticos auxiliares: `applyFilters`, `applySort`, `applyJoins`, `matchesFilterValue` (soporta eq, ne, gt, gte, lt, lte, contains, starts_with, ends_with, is_null, is_not_null, between, regex).
|
|
387
|
+
|
|
388
|
+
### Renderers
|
|
389
|
+
|
|
390
|
+
| Renderer | Archivo | Descripción |
|
|
391
|
+
|----------|---------|-------------|
|
|
392
|
+
| TextRenderer | `viewer/renderers/TextRenderer.vue` | Texto plano con soporte de highlight de búsqueda |
|
|
393
|
+
| ImageRenderer | `viewer/renderers/ImageRenderer.vue` | `<img>` con resolución de binding/URL |
|
|
394
|
+
| BarcodeRenderer | `viewer/renderers/BarcodeRenderer.vue` | SVG vía jsbarcode (CODE128 default) |
|
|
395
|
+
| QRCodeRenderer | `viewer/renderers/QRCodeRenderer.vue` | QR vía librería qrcode |
|
|
396
|
+
| ShapeRenderer | `viewer/renderers/ShapeRenderer.vue` | Rectángulo, elipse, línea, flecha (CSS inline) |
|
|
397
|
+
| DrillDownRenderer | `viewer/renderers/DrillDownRenderer.vue` | Elemento clickeable que emite `drill-click` |
|
|
398
|
+
| FallbackRenderer | `viewer/renderers/FallbackRenderer.vue` | Muestra tipo no soportado |
|
|
399
|
+
| SubreportRenderer | `viewer/renderers/SubreportRenderer.vue` | `<nexa-viewer>` anidado (embebido o remoto) |
|
|
400
|
+
| ChartRenderer | `viewer/ChartRenderer.vue` | ECharts (barras, líneas, pastel, etc.) |
|
|
401
|
+
| CrosstabRenderer | `viewer/CrosstabRenderer.vue` | Tabla dinámica con agrupación fila/columna |
|
|
402
|
+
| DashboardWidgetRenderer | `viewer/DashboardWidgetRenderer.vue` | Widgets en modo dashboard |
|
|
403
|
+
|
|
404
|
+
Total: **11 renderers** (8 en `viewer/renderers/` + 3 en `viewer/`).
|
|
405
|
+
|
|
406
|
+
### Zoom (`useZoom`)
|
|
407
|
+
|
|
408
|
+
- Rango: 25%–250%, default 100%
|
|
409
|
+
- `zoomIn()` / `zoomOut()` — ±10 puntos
|
|
410
|
+
- `fitToWidth()` — escala al ancho del viewport
|
|
411
|
+
- `fitToPage()` — escala al alto y ancho
|
|
412
|
+
- Modos: `continuous` (scroll vertical) / `single` (página por página)
|
|
413
|
+
- Atajo: Ctrl+`+`, Ctrl+`-`, Ctrl+`0`
|
|
414
|
+
|
|
415
|
+
### Búsqueda (`useSearch`)
|
|
416
|
+
|
|
417
|
+
- `searchQuery` con debounce de 200ms
|
|
418
|
+
- Escanea hasta 250 páginas, case-insensitive, máx 500 resultados
|
|
419
|
+
- Navegación: `nextMatch()` / `prevMatch()` con scroll automático
|
|
420
|
+
- Highlight: `getHighlightParts(text)` retorna segmentos marcados/no marcados
|
|
421
|
+
- Input de búsqueda en toolbar con atajo de teclado
|
|
422
|
+
|
|
423
|
+
### Watermarks
|
|
424
|
+
|
|
425
|
+
- **Tile**: texto repetido en grilla (20 spans CSS grid), opacidad configurable, rotado
|
|
426
|
+
- **Single**: posicionado absoluto (center, top-left, top-right, bottom-left, bottom-right, top-center, bottom-center)
|
|
427
|
+
- Opacidad, color, tamaño, rotación por configuración
|
|
428
|
+
|
|
429
|
+
### TOC (Tabla de Contenidos)
|
|
430
|
+
|
|
431
|
+
- Generación automática si `generateTOC: true` en la definición
|
|
432
|
+
- Se Renderiza en página 1
|
|
433
|
+
- Cada ítem: texto + dots + número de página
|
|
434
|
+
- Navegación: click → `goToPage(n)` con scroll
|
|
435
|
+
|
|
436
|
+
### Dashboard
|
|
437
|
+
|
|
438
|
+
Si `documentType === 'Dashboard'`, el reporte se Renderiza como grilla de widgets (`DashboardWidgetRenderer`) en lugar de páginas con bandas.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
298
442
|
## Formato de datos
|
|
299
443
|
|
|
300
|
-
### Array simple
|
|
444
|
+
### Array simple
|
|
301
445
|
|
|
302
446
|
```json
|
|
303
447
|
[
|
|
@@ -306,7 +450,9 @@ v.addEventListener('data-request', e => console.log(e.detail.alias));
|
|
|
306
450
|
]
|
|
307
451
|
```
|
|
308
452
|
|
|
309
|
-
|
|
453
|
+
El DataSource se asocia automáticamente al alias `"main"`.
|
|
454
|
+
|
|
455
|
+
### Múltiples DataSources
|
|
310
456
|
|
|
311
457
|
```json
|
|
312
458
|
{
|
|
@@ -314,13 +460,12 @@ v.addEventListener('data-request', e => console.log(e.detail.alias));
|
|
|
314
460
|
{ "id": 1, "nombre": "Juan", "ciudad": "Bogotá" }
|
|
315
461
|
],
|
|
316
462
|
"pedidos": [
|
|
317
|
-
{ "cliente_id": 1, "producto": "Camisa", "cantidad": 3 }
|
|
318
|
-
{ "cliente_id": 1, "producto": "Pantalón", "cantidad": 2 }
|
|
463
|
+
{ "cliente_id": 1, "producto": "Camisa", "cantidad": 3 }
|
|
319
464
|
]
|
|
320
465
|
}
|
|
321
466
|
```
|
|
322
467
|
|
|
323
|
-
|
|
468
|
+
Cada banda referencia su DataSource por alias: `dataSource: "clientes"`.
|
|
324
469
|
|
|
325
470
|
### Parámetros
|
|
326
471
|
|
|
@@ -365,35 +510,40 @@ viewer.parameters = {
|
|
|
365
510
|
| `{[IIF(total > 100000, 'ALTO', 'BAJO')]}` | `ALTO` |
|
|
366
511
|
| `{[ISNULL(cliente, 'Sin nombre')]}` | `Sin nombre` |
|
|
367
512
|
| `{[UPPER(nombre)]}` | `JUAN PÉREZ` |
|
|
513
|
+
| `{[LOWER(nombre)]}` | `juan pérez` |
|
|
368
514
|
| `{[SUBSTRING(texto, 0, 3)]}` | `Hel` |
|
|
369
|
-
| `{[
|
|
370
|
-
| `{[
|
|
515
|
+
| `{[LENGTH(nombre)]}` | `10` |
|
|
516
|
+
| `{[TRIM(texto)]}` | sin espacios |
|
|
517
|
+
| `{[ABS(-5)]}` | `5` |
|
|
518
|
+
| `{[CEIL(5.3)]}` | `6` |
|
|
519
|
+
| `{[FLOOR(5.9)]}` | `5` |
|
|
520
|
+
| `{[SUM(cantidad)]}` | suma |
|
|
521
|
+
| `{[COUNT(id)]}` | conteo |
|
|
522
|
+
| `{[AVG(precio)]}` | promedio |
|
|
523
|
+
| `{[MIN(precio)]}` | mínimo |
|
|
524
|
+
| `{[MAX(precio)]}` | máximo |
|
|
371
525
|
| `{[CONCAT(nombre, ' - ', ciudad)]}` | `Juan - Bogotá` |
|
|
526
|
+
| `{[REPLACE(texto, 'a', 'o')]}` | reemplazo |
|
|
372
527
|
|
|
373
528
|
---
|
|
374
529
|
|
|
375
530
|
## Ejemplos
|
|
376
531
|
|
|
377
|
-
Los reportes de ejemplo están en `examples/` y son compatibles
|
|
532
|
+
Los reportes de ejemplo están en `examples/` y son compatibles con diseñador y visor.
|
|
378
533
|
|
|
379
534
|
| Archivo | Descripción |
|
|
380
535
|
|---------|-------------|
|
|
381
536
|
| `report-factura.json` | Factura con ítems, cliente y totales |
|
|
382
|
-
| `factura_de_recolección_de_residuos.json` | Factura ambiental
|
|
537
|
+
| `factura_de_recolección_de_residuos.json` | Factura ambiental multi-DataSource |
|
|
383
538
|
| `report-factura-residuos.json` | Factura de residuos con GroupHeader/Footer |
|
|
384
|
-
| `report-anexo-factura.json` | Anexo
|
|
385
|
-
| `report-ventas-logo-tabla.json` |
|
|
386
|
-
| `report-agrupado-por-cliente.json` | GroupHeader +
|
|
539
|
+
| `report-anexo-factura.json` | Anexo con tabla expandida |
|
|
540
|
+
| `report-ventas-logo-tabla.json` | Corporativo con logo y tabla agrupada |
|
|
541
|
+
| `report-agrupado-por-cliente.json` | GroupHeader + agrupación por cliente |
|
|
387
542
|
| `report-productos.json` | Lista simple de productos |
|
|
388
|
-
| `report-crosstab-categoria-mes.json` |
|
|
389
|
-
| `report-grafico-ventas-por-mes.json` | Gráfico de barras
|
|
390
|
-
| `report-master-detail-ordenes.json` | Master-Detail con subreportes
|
|
391
|
-
| `report-codigos-qr-barcode.json` | Códigos QR y barras
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
# Probar un ejemplo local
|
|
395
|
-
curl -s https://raw.githubusercontent.com/nexabase/nexabase-report/main/examples/report-factura.json
|
|
396
|
-
```
|
|
543
|
+
| `report-crosstab-categoria-mes.json` | Crosstab categoría × mes |
|
|
544
|
+
| `report-grafico-ventas-por-mes.json` | Gráfico de barras ECharts |
|
|
545
|
+
| `report-master-detail-ordenes.json` | Master-Detail con subreportes |
|
|
546
|
+
| `report-codigos-qr-barcode.json` | Códigos QR y barras |
|
|
397
547
|
|
|
398
548
|
---
|
|
399
549
|
|
|
@@ -405,28 +555,28 @@ npm version patch|minor|major
|
|
|
405
555
|
npm publish --access public
|
|
406
556
|
```
|
|
407
557
|
|
|
408
|
-
|
|
558
|
+
CDN automático:
|
|
409
559
|
|
|
410
560
|
```html
|
|
411
561
|
<script src="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/nexabase-report.umd.js"></script>
|
|
412
562
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nexabase-report@0.2/dist/style.css">
|
|
413
563
|
```
|
|
414
564
|
|
|
415
|
-
Ver [`docs/PUBLISHING.md`](docs/PUBLISHING.md) para CI/CD y
|
|
565
|
+
Ver [`docs/PUBLISHING.md`](docs/PUBLISHING.md) para CI/CD y registro privado.
|
|
416
566
|
|
|
417
567
|
---
|
|
418
568
|
|
|
419
569
|
## Documentación adicional
|
|
420
570
|
|
|
421
|
-
- [API completa del Viewer](docs/VIEWER_API.md) —
|
|
422
|
-
- [API REST
|
|
571
|
+
- [API completa del Viewer](docs/VIEWER_API.md) — props, métodos, eventos, tipos
|
|
572
|
+
- [API REST](docs/API_REST.md) — endpoints serverless
|
|
423
573
|
- [Integración externa](docs/EXTERNAL_INTEGRATION.md) — ASP.NET, Django, Laravel, Rails
|
|
424
|
-
- [Plan de QA](docs/QA_PLAN.md) —
|
|
574
|
+
- [Plan de QA](docs/QA_PLAN.md) — checklist de pruebas manuales
|
|
425
575
|
- [Checklist de regresión exportación](docs/EXPORT_REGRESSION.md) — PDF, Excel, Word, CSV
|
|
426
|
-
- [Guía de publicación npm](docs/PUBLISHING.md) —
|
|
576
|
+
- [Guía de publicación npm](docs/PUBLISHING.md) — build, versionado, CI/CD
|
|
427
577
|
|
|
428
578
|
---
|
|
429
579
|
|
|
430
580
|
## Licencia
|
|
431
581
|
|
|
432
|
-
MIT © NexaBase Team
|
|
582
|
+
MIT © NexaBase Team
|
|
@@ -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-DLc-dsvr.js";
|
|
2
|
+
import { r as f } from "./html2canvas-B37RfnAd.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-DLc-dsvr.js";
|
|
2
|
+
import { j as Ge } from "./jspdf.es.min-P9H2mKOY.js";
|
|
3
|
+
import { r as Be } from "./html2canvas-B37RfnAd.js";
|
|
4
4
|
function Ue(ge, we) {
|
|
5
5
|
for (var me = 0; me < we.length; me++) {
|
|
6
6
|
const ce = we[me];
|