data-table-list 1.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +549 -2
- package/package.json +6 -10
- package/DOCUMENTATION.md +0 -561
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License Copyright (c) 2026 msalvo
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of
|
|
4
|
+
charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
(including the next paragraph) shall be included in all copies or substantial
|
|
13
|
+
portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
18
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Guida all'Utilizzo e all'Implementazione di `DataTableList`
|
|
2
2
|
|
|
3
3
|
`DataTableList` è un componente riutilizzabile sviluppato in **Vue 3** (con TypeScript e Composition API). Offre una tabella dati flessibile con supporto nativo per paginazione, ordinamento, filtraggio, azioni di riga, esportazione dati, e selezione multipla tramite checkbox.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
[](https://npmjs.com) [](https://npmjs.com)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
7
12
|
## Project Setup
|
|
8
13
|
|
|
9
14
|
```sh
|
|
10
|
-
npm install data-table-list
|
|
15
|
+
npm install data-table-list@latest
|
|
11
16
|
```
|
|
12
17
|
|
|
13
18
|
---
|
|
@@ -21,5 +26,547 @@ import type { DataTableConf } from 'data-table-list';
|
|
|
21
26
|
import 'data-table-list/dist/data-table-list.css'; // This imports the new stable CSS
|
|
22
27
|
|
|
23
28
|
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 1. Strutture Dati e Interfacce (Modelli)
|
|
34
|
+
|
|
35
|
+
Il componente utilizza le seguenti interfacce TypeScript definite in [Models.ts](file:///c:/Users/UTENTE/Desktop/progettoIA/data-table-list/src/model/Models.ts):
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
export interface DataTableConf {
|
|
39
|
+
columns: ColumnTable[]; // Configurazione delle colonne
|
|
40
|
+
actionsRow?: ActionRow; // Azioni di default abilitate per riga
|
|
41
|
+
viewActions: boolean; // Se mostrare o meno la colonna delle azioni
|
|
42
|
+
viewDownload?: boolean; // Se visualizzare il pulsante per il download/esportazione
|
|
43
|
+
paginator?: boolean; // Abilita la paginazione
|
|
44
|
+
paginatorPosition?: PaginatorPosition; // Posizione e allineamento del paginatore
|
|
45
|
+
isserver?: boolean; // Se la paginazione e il filtraggio sono gestiti lato server
|
|
46
|
+
serverPaging?: ServerPaging; // Metadati per la paginazione server-side
|
|
47
|
+
dimensionePagina: number; // Numero di righe per pagina
|
|
48
|
+
messageNotRecords?: string; // Messaggio mostrato se la tabella è vuota
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ColumnTable {
|
|
52
|
+
title: string; // Intestazione della colonna
|
|
53
|
+
property: string; // Nome della chiave nell'oggetto riga
|
|
54
|
+
style?: string | null; // Stile CSS inline da applicare alla cella (es: "width: 150px")
|
|
55
|
+
callFormatt?: Function | null; // Funzione custom per formattare il valore della cella
|
|
56
|
+
hidden?: boolean; // Nasconde la colonna se impostato a true
|
|
57
|
+
order?: boolean; // Abilita l'ordinamento client-side per questa colonna
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ActionRow {
|
|
61
|
+
view: boolean; // Mostra l'icona Visualizza (it-password-visible)
|
|
62
|
+
update: boolean; // Mostra l'icona Modifica (it-pencil)
|
|
63
|
+
delete: boolean; // Mostra l'icona Elimina (it-delete)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface PaginatorPosition {
|
|
67
|
+
top: boolean; // Mostra il paginatore sopra la tabella
|
|
68
|
+
bottom: boolean; // Mostra il paginatore sotto la tabella
|
|
69
|
+
class: string | 'center' | 'left' | 'right'; // Allineamento del paginatore ('left', 'center', 'right')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ServerPaging {
|
|
73
|
+
serverTotaleElementi: number; // Numero totale di elementi sul server
|
|
74
|
+
serverTotalePagine: number; // Numero totale di pagine sul server
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 2. API del Componente (Props ed Emits)
|
|
81
|
+
|
|
82
|
+
### Props (Proprietà)
|
|
83
|
+
|
|
84
|
+
| Nome Prop | Tipo | Obbligatorio | Descrizione |
|
|
85
|
+
| :--- | :--- | :--- | :--- |
|
|
86
|
+
| `dataTableConf` | `DataTableConf` | **Sì** | Configurazione della struttura e del comportamento della tabella. |
|
|
87
|
+
| `dataItems` | `any[]` | **Sì** | Array di dati (righe) da visualizzare. Se `isserver` è `false`, contiene tutti i record. Se `isserver` è `true`, contiene solo i record della pagina corrente. |
|
|
88
|
+
| `filter` | `string` | No | Stringa di ricerca globale. Se specificata e `isserver` è `false`, la tabella esegue un filtro client-side su tutte le colonne visibili. |
|
|
89
|
+
| `showCheckbox` | `boolean` | No | Se impostato a `true`, aggiunge una prima colonna con checkbox per la selezione multipla delle righe. |
|
|
90
|
+
|
|
91
|
+
### Emits (Eventi)
|
|
92
|
+
|
|
93
|
+
| Nome Evento | Parametri | Descrizione |
|
|
94
|
+
| :--- | :--- | :--- |
|
|
95
|
+
| `@onview` | `(obj: object)` | Emesso al click sul pulsante "Visualizza" della riga (default action). Riceve l'oggetto riga. |
|
|
96
|
+
| `@onupdate` | `(obj: object)` | Emesso al click sul pulsante "Modifica" della riga (default action). Riceve l'oggetto riga. |
|
|
97
|
+
| `@ondelete` | `(obj: object)` | Emesso al click sul pulsante "Elimina" della riga (default action). Riceve l'oggetto riga. |
|
|
98
|
+
| `@onavanti` | `(current: number, size: number)` | Emesso quando si va alla pagina successiva **solo se** `isserver` è `true`. `current` è l'indice di pagina (0-indexed), `size` è la dimensione della pagina. |
|
|
99
|
+
| `@onindietro` | `(current: number, size: number)` | Emesso quando si va alla pagina precedente **solo se** `isserver` è `true`. `current` è l'indice di pagina (0-indexed), `size` è la dimensione della pagina. |
|
|
100
|
+
| `@ondownload` | `(call: any[])` | Emesso al click sul pulsante di download. Riceve la lista corrente dei dati filtrati. |
|
|
101
|
+
| `@onchecked` | `(obj: any[])` | Emesso ogni volta che cambia la selezione delle righe tramite checkbox. Riceve l'array delle righe attualmente selezionate. |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 3. Slot di Personalizzazione
|
|
106
|
+
|
|
107
|
+
`DataTableList` offre diversi slot con scoped-slots per personalizzare profondamente il markup:
|
|
108
|
+
|
|
109
|
+
* **`#columnTd`**: Permette di personalizzare il rendering del contenuto di ogni cella. Riceve `{ row, column }` come parametri dello slot.
|
|
110
|
+
* **`#iconTd`**: Consente di posizionare un'icona o un elemento decorativo a fianco del valore della cella. Riceve `{ row, column }`.
|
|
111
|
+
* **`#actions`**: Sostituisce i pulsanti di default (Visualizza, Modifica, Elimina) con pulsanti personalizzati o menu dropdown. Riceve `{ row }`.
|
|
112
|
+
* **`#checkbox`**: Sostituisce il checkbox di default delle righe. Riceve `{ row }`.
|
|
113
|
+
* **`#download`**: Personalizza l'aspetto del pulsante di download. Riceve l'evento `{ ondownload: { oncall: Function } }`.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 4. Scenari d'Uso ed Esempi di Implementazione
|
|
118
|
+
|
|
119
|
+
### Scenario A: Tabella Semplice Client-Side con Paginazione e Ordinamento
|
|
120
|
+
In questo scenario, i dati sono interamente caricati in memoria. Il componente gestisce la paginazione, la ricerca globale e l'ordinamento in modo autonomo.
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<script setup lang="ts">
|
|
124
|
+
import { ref } from 'vue';
|
|
125
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
126
|
+
import type { DataTableConf } from 'data-table-list';
|
|
127
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
128
|
+
|
|
129
|
+
const searchFilter = ref('');
|
|
130
|
+
|
|
131
|
+
const tableConfig = ref<DataTableConf>({
|
|
132
|
+
columns: [
|
|
133
|
+
{ title: 'ID', property: 'id', order: true },
|
|
134
|
+
{ title: 'Nome', property: 'name', order: true },
|
|
135
|
+
{ title: 'Email', property: 'email', order: true }
|
|
136
|
+
],
|
|
137
|
+
isserver: false,
|
|
138
|
+
paginator: true,
|
|
139
|
+
paginatorPosition: { top: true, bottom: true, class: 'right' },
|
|
140
|
+
viewActions: true,
|
|
141
|
+
actionsRow: { view: true, update: true, delete: true },
|
|
142
|
+
dimensionePagina: 5,
|
|
143
|
+
messageNotRecords: 'Nessun utente trovato.'
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const items = ref([
|
|
147
|
+
{ id: 1, name: 'Mario Rossi', email: 'mario.rossi@example.com' },
|
|
148
|
+
{ id: 2, name: 'Giuseppe Verdi', email: 'giuseppe.verdi@example.com' },
|
|
149
|
+
{ id: 3, name: 'Luca Bianchi', email: 'luca.bianchi@example.com' },
|
|
150
|
+
{ id: 4, name: 'Anna Neri', email: 'anna.neri@example.com' },
|
|
151
|
+
{ id: 5, name: 'Sofia Gialli', email: 'sofia.gialli@example.com' },
|
|
152
|
+
{ id: 6, name: 'Elena Rosa', email: 'elena.rosa@example.com' }
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
const handleView = (item: any) => console.log('Visualizza:', item);
|
|
156
|
+
const handleUpdate = (item: any) => console.log('Modifica:', item);
|
|
157
|
+
const handleDelete = (item: any) => console.log('Elimina:', item);
|
|
158
|
+
</script>
|
|
159
|
+
|
|
160
|
+
<template>
|
|
161
|
+
<div class="container my-4">
|
|
162
|
+
<!-- Input di ricerca collegato alla prop :filter -->
|
|
163
|
+
<div class="form-group mb-3">
|
|
164
|
+
<label for="search">Cerca Utente</label>
|
|
165
|
+
<input id="search" v-model="searchFilter" type="text" class="form-control" placeholder="Cerca...">
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<DataTableList
|
|
169
|
+
:data-items="items"
|
|
170
|
+
:data-table-conf="tableConfig"
|
|
171
|
+
:filter="searchFilter"
|
|
172
|
+
@onview="handleView"
|
|
173
|
+
@onupdate="handleUpdate"
|
|
174
|
+
@ondelete="handleDelete"
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Scenario B: Formattazione Personalizzata di Colonne (`callFormatt`)
|
|
183
|
+
Quando si desidera applicare una formattazione al volo su una colonna (es. date, valute, o composizione stringhe) senza alterare i dati di origine, si può definire la funzione `callFormatt` nel modello `ColumnTable`.
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const tableConfig = ref<DataTableConf>({
|
|
187
|
+
columns: [
|
|
188
|
+
{ title: 'ID', property: 'id' },
|
|
189
|
+
{
|
|
190
|
+
title: 'Nome Completo',
|
|
191
|
+
property: 'name',
|
|
192
|
+
// callFormatt riceve il valore del campo specificato in property e l'intera riga
|
|
193
|
+
callFormatt: (value: any, row: any) => {
|
|
194
|
+
return `${row.name.toUpperCase()} (${row.role})`;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
title: 'Data Creazione',
|
|
199
|
+
property: 'createdAt',
|
|
200
|
+
callFormatt: (value: string) => {
|
|
201
|
+
return new Date(value).toLocaleDateString('it-IT');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
isserver: false,
|
|
206
|
+
viewActions: false,
|
|
207
|
+
dimensionePagina: 10
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const items = ref([
|
|
211
|
+
{ id: 1, name: 'Mario Rossi', role: 'Admin', createdAt: '2026-06-01T10:00:00Z' },
|
|
212
|
+
{ id: 2, name: 'Giuseppe Verdi', role: 'User', createdAt: '2026-06-03T15:30:00Z' }
|
|
213
|
+
]);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### Scenario C: Personalizzazione Celle con Slot (`#columnTd` & Componenti Custom)
|
|
219
|
+
Se si necessita di inserire markup HTML complesso (es. Badge di stato, icone, o componenti esterni come `Avatar.vue`), si utilizza lo slot `#columnTd`.
|
|
220
|
+
|
|
221
|
+
```html
|
|
222
|
+
<script setup lang="ts">
|
|
223
|
+
import { ref } from 'vue';
|
|
224
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
225
|
+
import type { DataTableConf } from 'data-table-list';
|
|
226
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
227
|
+
|
|
228
|
+
const tableConfig = ref<DataTableConf>({
|
|
229
|
+
columns: [
|
|
230
|
+
{ title: 'Avatar', property: 'avatar', style: 'width: 80px;' },
|
|
231
|
+
{ title: 'Dettagli Utente', property: 'details' },
|
|
232
|
+
{ title: 'Stato', property: 'status' }
|
|
233
|
+
],
|
|
234
|
+
isserver: false,
|
|
235
|
+
viewActions: false,
|
|
236
|
+
dimensionePagina: 5
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const items = ref([
|
|
240
|
+
{ id: 1, name: 'Mario Rossi', email: 'mario.rossi@example.com', status: 'Attivo', statusColor: 'bg-success' },
|
|
241
|
+
{ id: 2, name: 'Giuseppe Verdi', email: 'giuseppe.verdi@example.com', status: 'Sospeso', statusColor: 'bg-warning' }
|
|
242
|
+
]);
|
|
243
|
+
</script>
|
|
244
|
+
|
|
245
|
+
<template>
|
|
246
|
+
<DataTableList :data-items="items" :data-table-conf="tableConfig">
|
|
247
|
+
<!-- Utilizzo dello scoped slot per le celle -->
|
|
248
|
+
<template #columnTd="{ row, column }">
|
|
249
|
+
|
|
250
|
+
<!-- Cella Avatar -->
|
|
251
|
+
<div v-if="column.property === 'avatar'">
|
|
252
|
+
<Avatar
|
|
253
|
+
:label="row.name.charAt(0) + row.name.split(' ')[1]?.charAt(0)"
|
|
254
|
+
:background-dynamics="true"
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<!-- Cella Dettagli Utente -->
|
|
259
|
+
<div v-else-if="column.property === 'details'">
|
|
260
|
+
<strong>{{ row.name }}</strong><br>
|
|
261
|
+
<small class="text-secondary">{{ row.email }}</small>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<!-- Cella Stato -->
|
|
265
|
+
<div v-else-if="column.property === 'status'">
|
|
266
|
+
<span class="badge rounded-pill text-white" :class="row.statusColor">
|
|
267
|
+
{{ row.status }}
|
|
268
|
+
</span>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
</template>
|
|
272
|
+
</DataTableList>
|
|
273
|
+
</template>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### Scenario D: Selezione Multipla con Checkbox e Evento `@onchecked`
|
|
279
|
+
Il componente supporta la selezione di elementi tramite una colonna di checkbox abilitata tramite la prop `:show-checkbox="true"`.
|
|
280
|
+
|
|
281
|
+
```html
|
|
282
|
+
<script setup lang="ts">
|
|
283
|
+
import { ref } from 'vue';
|
|
284
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
285
|
+
import type { DataTableConf } from 'data-table-list';
|
|
286
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
287
|
+
|
|
288
|
+
const tableConfig = ref<DataTableConf>({
|
|
289
|
+
columns: [
|
|
290
|
+
{ title: 'ID', property: 'id' },
|
|
291
|
+
{ title: 'Prodotto', property: 'product' }
|
|
292
|
+
],
|
|
293
|
+
isserver: false,
|
|
294
|
+
viewActions: false,
|
|
295
|
+
dimensionePagina: 10
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const items = ref([
|
|
299
|
+
{ id: 101, product: 'Computer Laptop' },
|
|
300
|
+
{ id: 102, product: 'Monitor 4K' },
|
|
301
|
+
{ id: 103, product: 'Tastiera Meccanica' }
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
const selectedItems = ref<any[]>([]);
|
|
305
|
+
|
|
306
|
+
// Salva gli elementi selezionati nello stato locale
|
|
307
|
+
const handleChecked = (checkedRows: any[]) => {
|
|
308
|
+
selectedItems.value = checkedRows;
|
|
309
|
+
console.log('Prodotti selezionati:', selectedItems.value);
|
|
310
|
+
};
|
|
311
|
+
</script>
|
|
312
|
+
|
|
313
|
+
<template>
|
|
314
|
+
<div class="my-3">
|
|
315
|
+
<DataTableList
|
|
316
|
+
:data-items="items"
|
|
317
|
+
:data-table-conf="tableConfig"
|
|
318
|
+
:show-checkbox="true"
|
|
319
|
+
@onchecked="handleChecked"
|
|
320
|
+
/>
|
|
321
|
+
|
|
322
|
+
<div class="mt-3">
|
|
323
|
+
<h5>Elementi selezionati: {{ selectedItems.length }}</h5>
|
|
324
|
+
<ul>
|
|
325
|
+
<li v-for="item in selectedItems" :key="item.id">{{ item.product }}</li>
|
|
326
|
+
</ul>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</template>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### Scenario E: Menu Azioni Personalizzato (Dropdown Bootstrap Italia)
|
|
335
|
+
Invece di mostrare i tre pulsanti standard in linea (view, update, delete), è possibile personalizzare la colonna delle azioni inserendo un menu a comparsa (dropdown) in perfetto stile Bootstrap Italia utilizzando lo slot `#actions`.
|
|
336
|
+
|
|
337
|
+
```html
|
|
338
|
+
<script setup lang="ts">
|
|
339
|
+
import { ref } from 'vue';
|
|
340
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
341
|
+
import type { DataTableConf } from 'data-table-list';
|
|
342
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
343
|
+
|
|
344
|
+
const tableConfig = ref<DataTableConf>({
|
|
345
|
+
columns: [
|
|
346
|
+
{ title: 'ID', property: 'id' },
|
|
347
|
+
{ title: 'Documento', property: 'docName' }
|
|
348
|
+
],
|
|
349
|
+
isserver: false,
|
|
350
|
+
viewActions: true, // Deve essere true per abilitare la colonna azioni
|
|
351
|
+
dimensionePagina: 10
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const items = ref([
|
|
355
|
+
{ id: 1, docName: 'Fattura_2026_01.pdf' },
|
|
356
|
+
{ id: 2, docName: 'Contratto_Firmato.pdf' }
|
|
357
|
+
]);
|
|
358
|
+
|
|
359
|
+
const scaricaDocumento = (row: any) => console.log('Download', row.docName);
|
|
360
|
+
const archiviaDocumento = (row: any) => console.log('Archivia', row.id);
|
|
361
|
+
</script>
|
|
362
|
+
|
|
363
|
+
<template>
|
|
364
|
+
<DataTableList :data-items="items" :data-table-conf="tableConfig">
|
|
365
|
+
<!-- Personalizzazione colonna azioni tramite slot -->
|
|
366
|
+
<template #actions="{ row }">
|
|
367
|
+
<div class="dropdown dropstart text-center">
|
|
368
|
+
<a class="btn btn-dropdown dropdown-toggle" href="#" role="button" id="dropdownActions" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
369
|
+
<!-- Icona a tre pallini di Bootstrap Italia -->
|
|
370
|
+
<svg class="icon-expand icon icon-sm icon-secondary">
|
|
371
|
+
<use href="@/assets/svg/sprites.svg#it-more-items"></use>
|
|
372
|
+
</svg>
|
|
373
|
+
</a>
|
|
374
|
+
<div class="dropdown-menu" aria-labelledby="dropdownActions">
|
|
375
|
+
<div class="link-list-wrapper">
|
|
376
|
+
<ul class="link-list">
|
|
377
|
+
<li>
|
|
378
|
+
<a class="dropdown-item list-item" @click.prevent="scaricaDocumento(row)">
|
|
379
|
+
<span>
|
|
380
|
+
<svg class="icon icon-primary" aria-hidden="true">
|
|
381
|
+
<use href="@/assets/svg/sprites.svg#it-download"></use>
|
|
382
|
+
</svg> Scarica
|
|
383
|
+
</span>
|
|
384
|
+
</a>
|
|
385
|
+
</li>
|
|
386
|
+
<li>
|
|
387
|
+
<a class="dropdown-item list-item" @click.prevent="archiviaDocumento(row)">
|
|
388
|
+
<span>
|
|
389
|
+
<svg class="icon icon-primary" aria-hidden="true">
|
|
390
|
+
<use href="@/assets/svg/sprites.svg#it-box"></use>
|
|
391
|
+
</svg> Archivia
|
|
392
|
+
</span>
|
|
393
|
+
</a>
|
|
394
|
+
</li>
|
|
395
|
+
</ul>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
</template>
|
|
400
|
+
</DataTableList>
|
|
401
|
+
</template>
|
|
402
|
+
```
|
|
24
403
|
|
|
25
404
|
---
|
|
405
|
+
|
|
406
|
+
### Scenario F: Paginazione Server-Side (`isserver: true`)
|
|
407
|
+
Se la sorgente dati ha molti elementi, la paginazione deve avvenire sul server.
|
|
408
|
+
* `isserver` deve essere impostato a `true`.
|
|
409
|
+
* È necessario compilare `serverPaging` con i totali di elementi e pagine forniti dalle API.
|
|
410
|
+
* Bisogna ascoltare gli eventi `@onavanti` e `@onindietro` per richiedere la pagina corretta al server e aggiornare l'array `dataItems`.
|
|
411
|
+
|
|
412
|
+
```html
|
|
413
|
+
<script setup lang="ts">
|
|
414
|
+
import { ref, onMounted } from 'vue';
|
|
415
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
416
|
+
import type { DataTableConf } from 'data-table-list';
|
|
417
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
418
|
+
|
|
419
|
+
// Stato locale per i dati e il caricamento
|
|
420
|
+
const items = ref<any[]>([]);
|
|
421
|
+
const loading = ref(false);
|
|
422
|
+
|
|
423
|
+
const tableConfig = ref<DataTableConf>({
|
|
424
|
+
columns: [
|
|
425
|
+
{ title: 'ID', property: 'id' },
|
|
426
|
+
{ title: 'Descrizione', property: 'description' }
|
|
427
|
+
],
|
|
428
|
+
isserver: true, // Indica che paginazione e ricerca avvengono sul server
|
|
429
|
+
paginator: true,
|
|
430
|
+
paginatorPosition: { top: false, bottom: true, class: 'center' },
|
|
431
|
+
viewActions: false,
|
|
432
|
+
dimensionePagina: 10,
|
|
433
|
+
serverPaging: {
|
|
434
|
+
serverTotaleElementi: 0, // Inizializzato a 0, popolato in seguito
|
|
435
|
+
serverTotalePagine: 0
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Funzione simulata per caricare i dati dalle API
|
|
440
|
+
const fetchApiData = async (page: number, size: number) => {
|
|
441
|
+
loading.value = true;
|
|
442
|
+
try {
|
|
443
|
+
// Es. const res = await axios.get(`/api/items?page=${page}&size=${size}`);
|
|
444
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
445
|
+
|
|
446
|
+
const mockTotalElements = 50;
|
|
447
|
+
const mockTotalPages = Math.ceil(mockTotalElements / size);
|
|
448
|
+
|
|
449
|
+
// Creazione dati simulati per la pagina corrente (es. pagina 0 -> ID 1-10)
|
|
450
|
+
const rows = [];
|
|
451
|
+
const start = page * size;
|
|
452
|
+
for (let i = 0; i < size; i++) {
|
|
453
|
+
const id = start + i + 1;
|
|
454
|
+
if (id <= mockTotalElements) {
|
|
455
|
+
rows.push({ id, description: `Elemento server ${id}` });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
items.value = rows;
|
|
460
|
+
|
|
461
|
+
// Aggiornamento dei metadati di paginazione server-side
|
|
462
|
+
tableConfig.value.serverPaging = {
|
|
463
|
+
serverTotaleElementi: mockTotalElements,
|
|
464
|
+
serverTotalePagine: mockTotalPages
|
|
465
|
+
};
|
|
466
|
+
} finally {
|
|
467
|
+
loading.value = false;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// Gestione del cambio pagina in avanti
|
|
472
|
+
const handleAvanti = async (currentPage: number, size: number) => {
|
|
473
|
+
await fetchApiData(currentPage, size);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// Gestione del cambio pagina all'indietro
|
|
477
|
+
const handleIndietro = async (currentPage: number, size: number) => {
|
|
478
|
+
await fetchApiData(currentPage, size);
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
onMounted(async () => {
|
|
482
|
+
// Caricamento iniziale
|
|
483
|
+
await fetchApiData(0, tableConfig.value.dimensionePagina);
|
|
484
|
+
});
|
|
485
|
+
</script>
|
|
486
|
+
|
|
487
|
+
<template>
|
|
488
|
+
<div class="container my-4">
|
|
489
|
+
<h3>Paginazione Server-Side</h3>
|
|
490
|
+
<div v-if="loading" class="alert alert-info">Caricamento...</div>
|
|
491
|
+
|
|
492
|
+
<DataTableList
|
|
493
|
+
:data-items="items"
|
|
494
|
+
:data-table-conf="tableConfig"
|
|
495
|
+
@onavanti="handleAvanti"
|
|
496
|
+
@onindietro="handleIndietro"
|
|
497
|
+
/>
|
|
498
|
+
</div>
|
|
499
|
+
</template>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
### Scenario G: Esportazione e Download dei Dati Filtrati
|
|
505
|
+
Il componente offre la possibilità di abilitare un pulsante per il download dei dati.
|
|
506
|
+
* Impostare `viewDownload: true` nella configurazione `DataTableConf`.
|
|
507
|
+
* Ascoltare l'evento `@ondownload`, che riceve la lista dei record attualmente filtrati dalla ricerca globale (`filterComputer`).
|
|
508
|
+
|
|
509
|
+
```html
|
|
510
|
+
<script setup lang="ts">
|
|
511
|
+
import { ref } from 'vue';
|
|
512
|
+
import { DataTableList, Avatar } from 'data-table-list';
|
|
513
|
+
import type { DataTableConf } from 'data-table-list';
|
|
514
|
+
import 'data-table-list/dist/data-table-list.css';
|
|
515
|
+
|
|
516
|
+
const searchFilter = ref('');
|
|
517
|
+
|
|
518
|
+
const tableConfig = ref<DataTableConf>({
|
|
519
|
+
columns: [
|
|
520
|
+
{ title: 'ID', property: 'id' },
|
|
521
|
+
{ title: 'Nome', property: 'name' }
|
|
522
|
+
],
|
|
523
|
+
isserver: false,
|
|
524
|
+
viewActions: false,
|
|
525
|
+
viewDownload: true, // Mostra l'icona per il download in alto a destra
|
|
526
|
+
dimensionePagina: 10
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const items = ref([
|
|
530
|
+
{ id: 1, name: 'Mario Rossi' },
|
|
531
|
+
{ id: 2, name: 'Giuseppe Verdi' },
|
|
532
|
+
{ id: 3, name: 'Luca Bianchi' }
|
|
533
|
+
]);
|
|
534
|
+
|
|
535
|
+
// Funzione di esportazione CSV client-side al click sul download
|
|
536
|
+
const handleDownload = (filteredData: any[]) => {
|
|
537
|
+
const csvHeaders = 'ID,Nome\n';
|
|
538
|
+
const csvRows = filteredData.map(item => `${item.id},"${item.name}"`).join('\n');
|
|
539
|
+
const csvContent = 'data:text/csv;charset=utf-8,' + csvHeaders + csvRows;
|
|
540
|
+
|
|
541
|
+
const encodedUri = encodeURI(csvContent);
|
|
542
|
+
const link = document.createElement('a');
|
|
543
|
+
link.setAttribute('href', encodedUri);
|
|
544
|
+
link.setAttribute('download', 'esportazione_tabella.csv');
|
|
545
|
+
document.body.appendChild(link);
|
|
546
|
+
link.click();
|
|
547
|
+
document.body.removeChild(link);
|
|
548
|
+
};
|
|
549
|
+
</script>
|
|
550
|
+
|
|
551
|
+
<template>
|
|
552
|
+
<div class="container my-4">
|
|
553
|
+
<div class="form-group mb-3">
|
|
554
|
+
<input v-model="searchFilter" type="text" class="form-control" placeholder="Cerca...">
|
|
555
|
+
</div>
|
|
556
|
+
|
|
557
|
+
<DataTableList
|
|
558
|
+
:data-items="items"
|
|
559
|
+
:data-table-conf="tableConfig"
|
|
560
|
+
:filter="searchFilter"
|
|
561
|
+
@ondownload="handleDownload"
|
|
562
|
+
/>
|
|
563
|
+
</div>
|
|
564
|
+
</template>
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## 5. Dettagli Implementativi Importanti
|
|
570
|
+
|
|
571
|
+
1. **Identificativi Checkbox (`checkId`)**: Quando viene attivata la funzionalità checkbox (`showCheckbox = true`), il componente genera temporaneamente la chiave `checkId` all'interno dei record dell'array `dataItems` per identificare univocamente le righe selezionate. L'array restituito dall'evento `@onchecked` conterrà questi campi aggiuntivi.
|
|
572
|
+
2. **Reset dell'Ordinamento al cambio pagina**: Ad ogni navigazione di pagina (avanti o indietro) o reset del filtro di ricerca, viene richiamata la funzione `resetClassOrder()` che rimuove la classe CSS `.icon-primaryOrder` dall'header della colonna ordinata, indicando che la nuova pagina mostra i dati nel loro stato originario.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-table-list",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/data-table-list.umd.cjs",
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
"./dist/data-table-list.css": "./dist/data-table-list.css"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
"DOCUMENTATION.md"
|
|
18
|
+
"dist"
|
|
20
19
|
],
|
|
21
20
|
"scripts": {
|
|
22
21
|
"dev": "vite",
|
|
@@ -27,14 +26,10 @@
|
|
|
27
26
|
"prepare": "npm run build"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
"vue": "^3.5.32",
|
|
32
|
-
"vue-router": "^4.6.4"
|
|
29
|
+
"vue": "^3.5.32"
|
|
33
30
|
},
|
|
34
31
|
"peerDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"vue": "^3.5.32",
|
|
37
|
-
"vue-router": "^4.6.4"
|
|
32
|
+
"vue": "^3.5.32"
|
|
38
33
|
},
|
|
39
34
|
"devDependencies": {
|
|
40
35
|
"@microsoft/api-extractor": "^7.58.7",
|
|
@@ -51,5 +46,6 @@
|
|
|
51
46
|
},
|
|
52
47
|
"engines": {
|
|
53
48
|
"node": "^20.19.0 || >=22.12.0"
|
|
54
|
-
}
|
|
49
|
+
},
|
|
50
|
+
"license": "MIT"
|
|
55
51
|
}
|
package/DOCUMENTATION.md
DELETED
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
# Guida all'Utilizzo e all'Implementazione di `DataTableList`
|
|
2
|
-
|
|
3
|
-
`DataTableList` è un componente riutilizzabile sviluppato in **Vue 3** (con TypeScript e Composition API). Offre una tabella dati flessibile con supporto nativo per paginazione, ordinamento, filtraggio, azioni di riga, esportazione dati, e selezione multipla tramite checkbox.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Project Setup
|
|
8
|
-
|
|
9
|
-
```sh
|
|
10
|
-
npm install data-table-list@1.0.0
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## usage
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import { DataTableList, Avatar } from 'data-table-list';
|
|
20
|
-
import type { DataTableConf } from 'data-table-list';
|
|
21
|
-
import 'data-table-list/dist/data-table-list.css'; // This imports the new stable CSS
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 1. Strutture Dati e Interfacce (Modelli)
|
|
28
|
-
|
|
29
|
-
Il componente utilizza le seguenti interfacce TypeScript definite in [Models.ts](file:///c:/Users/UTENTE/Desktop/progettoIA/data-table-list/src/model/Models.ts):
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
export interface DataTableConf {
|
|
33
|
-
columns: ColumnTable[]; // Configurazione delle colonne
|
|
34
|
-
actionsRow?: ActionRow; // Azioni di default abilitate per riga
|
|
35
|
-
viewActions: boolean; // Se mostrare o meno la colonna delle azioni
|
|
36
|
-
viewDownload?: boolean; // Se visualizzare il pulsante per il download/esportazione
|
|
37
|
-
paginator?: boolean; // Abilita la paginazione
|
|
38
|
-
paginatorPosition?: PaginatorPosition; // Posizione e allineamento del paginatore
|
|
39
|
-
isserver?: boolean; // Se la paginazione e il filtraggio sono gestiti lato server
|
|
40
|
-
serverPaging?: ServerPaging; // Metadati per la paginazione server-side
|
|
41
|
-
dimensionePagina: number; // Numero di righe per pagina
|
|
42
|
-
messageNotRecords?: string; // Messaggio mostrato se la tabella è vuota
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ColumnTable {
|
|
46
|
-
title: string; // Intestazione della colonna
|
|
47
|
-
property: string; // Nome della chiave nell'oggetto riga
|
|
48
|
-
style?: string | null; // Stile CSS inline da applicare alla cella (es: "width: 150px")
|
|
49
|
-
callFormatt?: Function | null; // Funzione custom per formattare il valore della cella
|
|
50
|
-
hidden?: boolean; // Nasconde la colonna se impostato a true
|
|
51
|
-
order?: boolean; // Abilita l'ordinamento client-side per questa colonna
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ActionRow {
|
|
55
|
-
view: boolean; // Mostra l'icona Visualizza (it-password-visible)
|
|
56
|
-
update: boolean; // Mostra l'icona Modifica (it-pencil)
|
|
57
|
-
delete: boolean; // Mostra l'icona Elimina (it-delete)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface PaginatorPosition {
|
|
61
|
-
top: boolean; // Mostra il paginatore sopra la tabella
|
|
62
|
-
bottom: boolean; // Mostra il paginatore sotto la tabella
|
|
63
|
-
class: string | 'center' | 'left' | 'right'; // Allineamento del paginatore ('left', 'center', 'right')
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface ServerPaging {
|
|
67
|
-
serverTotaleElementi: number; // Numero totale di elementi sul server
|
|
68
|
-
serverTotalePagine: number; // Numero totale di pagine sul server
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 2. API del Componente (Props ed Emits)
|
|
75
|
-
|
|
76
|
-
### Props (Proprietà)
|
|
77
|
-
|
|
78
|
-
| Nome Prop | Tipo | Obbligatorio | Descrizione |
|
|
79
|
-
| :--- | :--- | :--- | :--- |
|
|
80
|
-
| `dataTableConf` | `DataTableConf` | **Sì** | Configurazione della struttura e del comportamento della tabella. |
|
|
81
|
-
| `dataItems` | `any[]` | **Sì** | Array di dati (righe) da visualizzare. Se `isserver` è `false`, contiene tutti i record. Se `isserver` è `true`, contiene solo i record della pagina corrente. |
|
|
82
|
-
| `filter` | `string` | No | Stringa di ricerca globale. Se specificata e `isserver` è `false`, la tabella esegue un filtro client-side su tutte le colonne visibili. |
|
|
83
|
-
| `showCheckbox` | `boolean` | No | Se impostato a `true`, aggiunge una prima colonna con checkbox per la selezione multipla delle righe. |
|
|
84
|
-
|
|
85
|
-
### Emits (Eventi)
|
|
86
|
-
|
|
87
|
-
| Nome Evento | Parametri | Descrizione |
|
|
88
|
-
| :--- | :--- | :--- |
|
|
89
|
-
| `@onview` | `(obj: object)` | Emesso al click sul pulsante "Visualizza" della riga (default action). Riceve l'oggetto riga. |
|
|
90
|
-
| `@onupdate` | `(obj: object)` | Emesso al click sul pulsante "Modifica" della riga (default action). Riceve l'oggetto riga. |
|
|
91
|
-
| `@ondelete` | `(obj: object)` | Emesso al click sul pulsante "Elimina" della riga (default action). Riceve l'oggetto riga. |
|
|
92
|
-
| `@onavanti` | `(current: number, size: number)` | Emesso quando si va alla pagina successiva **solo se** `isserver` è `true`. `current` è l'indice di pagina (0-indexed), `size` è la dimensione della pagina. |
|
|
93
|
-
| `@onindietro` | `(current: number, size: number)` | Emesso quando si va alla pagina precedente **solo se** `isserver` è `true`. `current` è l'indice di pagina (0-indexed), `size` è la dimensione della pagina. |
|
|
94
|
-
| `@ondownload` | `(call: any[])` | Emesso al click sul pulsante di download. Riceve la lista corrente dei dati filtrati. |
|
|
95
|
-
| `@onchecked` | `(obj: any[])` | Emesso ogni volta che cambia la selezione delle righe tramite checkbox. Riceve l'array delle righe attualmente selezionate. |
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## 3. Slot di Personalizzazione
|
|
100
|
-
|
|
101
|
-
`DataTableList` offre diversi slot con scoped-slots per personalizzare profondamente il markup:
|
|
102
|
-
|
|
103
|
-
* **`#columnTd`**: Permette di personalizzare il rendering del contenuto di ogni cella. Riceve `{ row, column }` come parametri dello slot.
|
|
104
|
-
* **`#iconTd`**: Consente di posizionare un'icona o un elemento decorativo a fianco del valore della cella. Riceve `{ row, column }`.
|
|
105
|
-
* **`#actions`**: Sostituisce i pulsanti di default (Visualizza, Modifica, Elimina) con pulsanti personalizzati o menu dropdown. Riceve `{ row }`.
|
|
106
|
-
* **`#checkbox`**: Sostituisce il checkbox di default delle righe. Riceve `{ row }`.
|
|
107
|
-
* **`#download`**: Personalizza l'aspetto del pulsante di download. Riceve l'evento `{ ondownload: { oncall: Function } }`.
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## 4. Scenari d'Uso ed Esempi di Implementazione
|
|
112
|
-
|
|
113
|
-
### Scenario A: Tabella Semplice Client-Side con Paginazione e Ordinamento
|
|
114
|
-
In questo scenario, i dati sono interamente caricati in memoria. Il componente gestisce la paginazione, la ricerca globale e l'ordinamento in modo autonomo.
|
|
115
|
-
|
|
116
|
-
```html
|
|
117
|
-
<script setup lang="ts">
|
|
118
|
-
import { ref } from 'vue';
|
|
119
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
120
|
-
import type { DataTableConf } from '@/model/Models';
|
|
121
|
-
|
|
122
|
-
const searchFilter = ref('');
|
|
123
|
-
|
|
124
|
-
const tableConfig = ref<DataTableConf>({
|
|
125
|
-
columns: [
|
|
126
|
-
{ title: 'ID', property: 'id', order: true },
|
|
127
|
-
{ title: 'Nome', property: 'name', order: true },
|
|
128
|
-
{ title: 'Email', property: 'email', order: true }
|
|
129
|
-
],
|
|
130
|
-
isserver: false,
|
|
131
|
-
paginator: true,
|
|
132
|
-
paginatorPosition: { top: true, bottom: true, class: 'right' },
|
|
133
|
-
viewActions: true,
|
|
134
|
-
actionsRow: { view: true, update: true, delete: true },
|
|
135
|
-
dimensionePagina: 5,
|
|
136
|
-
messageNotRecords: 'Nessun utente trovato.'
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const items = ref([
|
|
140
|
-
{ id: 1, name: 'Mario Rossi', email: 'mario.rossi@example.com' },
|
|
141
|
-
{ id: 2, name: 'Giuseppe Verdi', email: 'giuseppe.verdi@example.com' },
|
|
142
|
-
{ id: 3, name: 'Luca Bianchi', email: 'luca.bianchi@example.com' },
|
|
143
|
-
{ id: 4, name: 'Anna Neri', email: 'anna.neri@example.com' },
|
|
144
|
-
{ id: 5, name: 'Sofia Gialli', email: 'sofia.gialli@example.com' },
|
|
145
|
-
{ id: 6, name: 'Elena Rosa', email: 'elena.rosa@example.com' }
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
const handleView = (item: any) => console.log('Visualizza:', item);
|
|
149
|
-
const handleUpdate = (item: any) => console.log('Modifica:', item);
|
|
150
|
-
const handleDelete = (item: any) => console.log('Elimina:', item);
|
|
151
|
-
</script>
|
|
152
|
-
|
|
153
|
-
<template>
|
|
154
|
-
<div class="container my-4">
|
|
155
|
-
<!-- Input di ricerca collegato alla prop :filter -->
|
|
156
|
-
<div class="form-group mb-3">
|
|
157
|
-
<label for="search">Cerca Utente</label>
|
|
158
|
-
<input id="search" v-model="searchFilter" type="text" class="form-control" placeholder="Cerca...">
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
<DataTableList
|
|
162
|
-
:data-items="items"
|
|
163
|
-
:data-table-conf="tableConfig"
|
|
164
|
-
:filter="searchFilter"
|
|
165
|
-
@onview="handleView"
|
|
166
|
-
@onupdate="handleUpdate"
|
|
167
|
-
@ondelete="handleDelete"
|
|
168
|
-
/>
|
|
169
|
-
</div>
|
|
170
|
-
</template>
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
### Scenario B: Formattazione Personalizzata di Colonne (`callFormatt`)
|
|
176
|
-
Quando si desidera applicare una formattazione al volo su una colonna (es. date, valute, o composizione stringhe) senza alterare i dati di origine, si può definire la funzione `callFormatt` nel modello `ColumnTable`.
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
const tableConfig = ref<DataTableConf>({
|
|
180
|
-
columns: [
|
|
181
|
-
{ title: 'ID', property: 'id' },
|
|
182
|
-
{
|
|
183
|
-
title: 'Nome Completo',
|
|
184
|
-
property: 'name',
|
|
185
|
-
// callFormatt riceve il valore del campo specificato in property e l'intera riga
|
|
186
|
-
callFormatt: (value: any, row: any) => {
|
|
187
|
-
return `${row.name.toUpperCase()} (${row.role})`;
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
title: 'Data Creazione',
|
|
192
|
-
property: 'createdAt',
|
|
193
|
-
callFormatt: (value: string) => {
|
|
194
|
-
return new Date(value).toLocaleDateString('it-IT');
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
],
|
|
198
|
-
isserver: false,
|
|
199
|
-
viewActions: false,
|
|
200
|
-
dimensionePagina: 10
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const items = ref([
|
|
204
|
-
{ id: 1, name: 'Mario Rossi', role: 'Admin', createdAt: '2026-06-01T10:00:00Z' },
|
|
205
|
-
{ id: 2, name: 'Giuseppe Verdi', role: 'User', createdAt: '2026-06-03T15:30:00Z' }
|
|
206
|
-
]);
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
### Scenario C: Personalizzazione Celle con Slot (`#columnTd` & Componenti Custom)
|
|
212
|
-
Se si necessita di inserire markup HTML complesso (es. Badge di stato, icone, o componenti esterni come `Avatar.vue`), si utilizza lo slot `#columnTd`.
|
|
213
|
-
|
|
214
|
-
```html
|
|
215
|
-
<script setup lang="ts">
|
|
216
|
-
import { ref } from 'vue';
|
|
217
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
218
|
-
import Avatar from '@/component/commons/Avatar.vue';
|
|
219
|
-
import type { DataTableConf } from '@/model/Models';
|
|
220
|
-
|
|
221
|
-
const tableConfig = ref<DataTableConf>({
|
|
222
|
-
columns: [
|
|
223
|
-
{ title: 'Avatar', property: 'avatar', style: 'width: 80px;' },
|
|
224
|
-
{ title: 'Dettagli Utente', property: 'details' },
|
|
225
|
-
{ title: 'Stato', property: 'status' }
|
|
226
|
-
],
|
|
227
|
-
isserver: false,
|
|
228
|
-
viewActions: false,
|
|
229
|
-
dimensionePagina: 5
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const items = ref([
|
|
233
|
-
{ id: 1, name: 'Mario Rossi', email: 'mario.rossi@example.com', status: 'Attivo', statusColor: 'bg-success' },
|
|
234
|
-
{ id: 2, name: 'Giuseppe Verdi', email: 'giuseppe.verdi@example.com', status: 'Sospeso', statusColor: 'bg-warning' }
|
|
235
|
-
]);
|
|
236
|
-
</script>
|
|
237
|
-
|
|
238
|
-
<template>
|
|
239
|
-
<DataTableList :data-items="items" :data-table-conf="tableConfig">
|
|
240
|
-
<!-- Utilizzo dello scoped slot per le celle -->
|
|
241
|
-
<template #columnTd="{ row, column }">
|
|
242
|
-
|
|
243
|
-
<!-- Cella Avatar -->
|
|
244
|
-
<div v-if="column.property === 'avatar'">
|
|
245
|
-
<Avatar
|
|
246
|
-
:label="row.name.charAt(0) + row.name.split(' ')[1]?.charAt(0)"
|
|
247
|
-
:background-dynamics="true"
|
|
248
|
-
/>
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<!-- Cella Dettagli Utente -->
|
|
252
|
-
<div v-else-if="column.property === 'details'">
|
|
253
|
-
<strong>{{ row.name }}</strong><br>
|
|
254
|
-
<small class="text-secondary">{{ row.email }}</small>
|
|
255
|
-
</div>
|
|
256
|
-
|
|
257
|
-
<!-- Cella Stato -->
|
|
258
|
-
<div v-else-if="column.property === 'status'">
|
|
259
|
-
<span class="badge rounded-pill text-white" :class="row.statusColor">
|
|
260
|
-
{{ row.status }}
|
|
261
|
-
</span>
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
</template>
|
|
265
|
-
</DataTableList>
|
|
266
|
-
</template>
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
### Scenario D: Selezione Multipla con Checkbox e Evento `@onchecked`
|
|
272
|
-
Il componente supporta la selezione di elementi tramite una colonna di checkbox abilitata tramite la prop `:show-checkbox="true"`.
|
|
273
|
-
|
|
274
|
-
```html
|
|
275
|
-
<script setup lang="ts">
|
|
276
|
-
import { ref } from 'vue';
|
|
277
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
278
|
-
import type { DataTableConf } from '@/model/Models';
|
|
279
|
-
|
|
280
|
-
const tableConfig = ref<DataTableConf>({
|
|
281
|
-
columns: [
|
|
282
|
-
{ title: 'ID', property: 'id' },
|
|
283
|
-
{ title: 'Prodotto', property: 'product' }
|
|
284
|
-
],
|
|
285
|
-
isserver: false,
|
|
286
|
-
viewActions: false,
|
|
287
|
-
dimensionePagina: 10
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
const items = ref([
|
|
291
|
-
{ id: 101, product: 'Computer Laptop' },
|
|
292
|
-
{ id: 102, product: 'Monitor 4K' },
|
|
293
|
-
{ id: 103, product: 'Tastiera Meccanica' }
|
|
294
|
-
]);
|
|
295
|
-
|
|
296
|
-
const selectedItems = ref<any[]>([]);
|
|
297
|
-
|
|
298
|
-
// Salva gli elementi selezionati nello stato locale
|
|
299
|
-
const handleChecked = (checkedRows: any[]) => {
|
|
300
|
-
selectedItems.value = checkedRows;
|
|
301
|
-
console.log('Prodotti selezionati:', selectedItems.value);
|
|
302
|
-
};
|
|
303
|
-
</script>
|
|
304
|
-
|
|
305
|
-
<template>
|
|
306
|
-
<div class="my-3">
|
|
307
|
-
<DataTableList
|
|
308
|
-
:data-items="items"
|
|
309
|
-
:data-table-conf="tableConfig"
|
|
310
|
-
:show-checkbox="true"
|
|
311
|
-
@onchecked="handleChecked"
|
|
312
|
-
/>
|
|
313
|
-
|
|
314
|
-
<div class="mt-3">
|
|
315
|
-
<h5>Elementi selezionati: {{ selectedItems.length }}</h5>
|
|
316
|
-
<ul>
|
|
317
|
-
<li v-for="item in selectedItems" :key="item.id">{{ item.product }}</li>
|
|
318
|
-
</ul>
|
|
319
|
-
</div>
|
|
320
|
-
</div>
|
|
321
|
-
</template>
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
### Scenario E: Menu Azioni Personalizzato (Dropdown Bootstrap Italia)
|
|
327
|
-
Invece di mostrare i tre pulsanti standard in linea (view, update, delete), è possibile personalizzare la colonna delle azioni inserendo un menu a comparsa (dropdown) in perfetto stile Bootstrap Italia utilizzando lo slot `#actions`.
|
|
328
|
-
|
|
329
|
-
```html
|
|
330
|
-
<script setup lang="ts">
|
|
331
|
-
import { ref } from 'vue';
|
|
332
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
333
|
-
import type { DataTableConf } from '@/model/Models';
|
|
334
|
-
|
|
335
|
-
const tableConfig = ref<DataTableConf>({
|
|
336
|
-
columns: [
|
|
337
|
-
{ title: 'ID', property: 'id' },
|
|
338
|
-
{ title: 'Documento', property: 'docName' }
|
|
339
|
-
],
|
|
340
|
-
isserver: false,
|
|
341
|
-
viewActions: true, // Deve essere true per abilitare la colonna azioni
|
|
342
|
-
dimensionePagina: 10
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
const items = ref([
|
|
346
|
-
{ id: 1, docName: 'Fattura_2026_01.pdf' },
|
|
347
|
-
{ id: 2, docName: 'Contratto_Firmato.pdf' }
|
|
348
|
-
]);
|
|
349
|
-
|
|
350
|
-
const scaricaDocumento = (row: any) => console.log('Download', row.docName);
|
|
351
|
-
const archiviaDocumento = (row: any) => console.log('Archivia', row.id);
|
|
352
|
-
</script>
|
|
353
|
-
|
|
354
|
-
<template>
|
|
355
|
-
<DataTableList :data-items="items" :data-table-conf="tableConfig">
|
|
356
|
-
<!-- Personalizzazione colonna azioni tramite slot -->
|
|
357
|
-
<template #actions="{ row }">
|
|
358
|
-
<div class="dropdown dropstart text-center">
|
|
359
|
-
<a class="btn btn-dropdown dropdown-toggle" href="#" role="button" id="dropdownActions" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
360
|
-
<!-- Icona a tre pallini di Bootstrap Italia -->
|
|
361
|
-
<svg class="icon-expand icon icon-sm icon-secondary">
|
|
362
|
-
<use href="@/assets/svg/sprites.svg#it-more-items"></use>
|
|
363
|
-
</svg>
|
|
364
|
-
</a>
|
|
365
|
-
<div class="dropdown-menu" aria-labelledby="dropdownActions">
|
|
366
|
-
<div class="link-list-wrapper">
|
|
367
|
-
<ul class="link-list">
|
|
368
|
-
<li>
|
|
369
|
-
<a class="dropdown-item list-item" @click.prevent="scaricaDocumento(row)">
|
|
370
|
-
<span>
|
|
371
|
-
<svg class="icon icon-primary" aria-hidden="true">
|
|
372
|
-
<use href="@/assets/svg/sprites.svg#it-download"></use>
|
|
373
|
-
</svg> Scarica
|
|
374
|
-
</span>
|
|
375
|
-
</a>
|
|
376
|
-
</li>
|
|
377
|
-
<li>
|
|
378
|
-
<a class="dropdown-item list-item" @click.prevent="archiviaDocumento(row)">
|
|
379
|
-
<span>
|
|
380
|
-
<svg class="icon icon-primary" aria-hidden="true">
|
|
381
|
-
<use href="@/assets/svg/sprites.svg#it-box"></use>
|
|
382
|
-
</svg> Archivia
|
|
383
|
-
</span>
|
|
384
|
-
</a>
|
|
385
|
-
</li>
|
|
386
|
-
</ul>
|
|
387
|
-
</div>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
</template>
|
|
391
|
-
</DataTableList>
|
|
392
|
-
</template>
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
### Scenario F: Paginazione Server-Side (`isserver: true`)
|
|
398
|
-
Se la sorgente dati ha molti elementi, la paginazione deve avvenire sul server.
|
|
399
|
-
* `isserver` deve essere impostato a `true`.
|
|
400
|
-
* È necessario compilare `serverPaging` con i totali di elementi e pagine forniti dalle API.
|
|
401
|
-
* Bisogna ascoltare gli eventi `@onavanti` e `@onindietro` per richiedere la pagina corretta al server e aggiornare l'array `dataItems`.
|
|
402
|
-
|
|
403
|
-
```html
|
|
404
|
-
<script setup lang="ts">
|
|
405
|
-
import { ref, onMounted } from 'vue';
|
|
406
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
407
|
-
import type { DataTableConf } from '@/model/Models';
|
|
408
|
-
|
|
409
|
-
// Stato locale per i dati e il caricamento
|
|
410
|
-
const items = ref<any[]>([]);
|
|
411
|
-
const loading = ref(false);
|
|
412
|
-
|
|
413
|
-
const tableConfig = ref<DataTableConf>({
|
|
414
|
-
columns: [
|
|
415
|
-
{ title: 'ID', property: 'id' },
|
|
416
|
-
{ title: 'Descrizione', property: 'description' }
|
|
417
|
-
],
|
|
418
|
-
isserver: true, // Indica che paginazione e ricerca avvengono sul server
|
|
419
|
-
paginator: true,
|
|
420
|
-
paginatorPosition: { top: false, bottom: true, class: 'center' },
|
|
421
|
-
viewActions: false,
|
|
422
|
-
dimensionePagina: 10,
|
|
423
|
-
serverPaging: {
|
|
424
|
-
serverTotaleElementi: 0, // Inizializzato a 0, popolato in seguito
|
|
425
|
-
serverTotalePagine: 0
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// Funzione simulata per caricare i dati dalle API
|
|
430
|
-
const fetchApiData = async (page: number, size: number) => {
|
|
431
|
-
loading.value = true;
|
|
432
|
-
try {
|
|
433
|
-
// Es. const res = await axios.get(`/api/items?page=${page}&size=${size}`);
|
|
434
|
-
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
435
|
-
|
|
436
|
-
const mockTotalElements = 50;
|
|
437
|
-
const mockTotalPages = Math.ceil(mockTotalElements / size);
|
|
438
|
-
|
|
439
|
-
// Creazione dati simulati per la pagina corrente (es. pagina 0 -> ID 1-10)
|
|
440
|
-
const rows = [];
|
|
441
|
-
const start = page * size;
|
|
442
|
-
for (let i = 0; i < size; i++) {
|
|
443
|
-
const id = start + i + 1;
|
|
444
|
-
if (id <= mockTotalElements) {
|
|
445
|
-
rows.push({ id, description: `Elemento server ${id}` });
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
items.value = rows;
|
|
450
|
-
|
|
451
|
-
// Aggiornamento dei metadati di paginazione server-side
|
|
452
|
-
tableConfig.value.serverPaging = {
|
|
453
|
-
serverTotaleElementi: mockTotalElements,
|
|
454
|
-
serverTotalePagine: mockTotalPages
|
|
455
|
-
};
|
|
456
|
-
} finally {
|
|
457
|
-
loading.value = false;
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
// Gestione del cambio pagina in avanti
|
|
462
|
-
const handleAvanti = async (currentPage: number, size: number) => {
|
|
463
|
-
await fetchApiData(currentPage, size);
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// Gestione del cambio pagina all'indietro
|
|
467
|
-
const handleIndietro = async (currentPage: number, size: number) => {
|
|
468
|
-
await fetchApiData(currentPage, size);
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
onMounted(async () => {
|
|
472
|
-
// Caricamento iniziale
|
|
473
|
-
await fetchApiData(0, tableConfig.value.dimensionePagina);
|
|
474
|
-
});
|
|
475
|
-
</script>
|
|
476
|
-
|
|
477
|
-
<template>
|
|
478
|
-
<div class="container my-4">
|
|
479
|
-
<h3>Paginazione Server-Side</h3>
|
|
480
|
-
<div v-if="loading" class="alert alert-info">Caricamento...</div>
|
|
481
|
-
|
|
482
|
-
<DataTableList
|
|
483
|
-
:data-items="items"
|
|
484
|
-
:data-table-conf="tableConfig"
|
|
485
|
-
@onavanti="handleAvanti"
|
|
486
|
-
@onindietro="handleIndietro"
|
|
487
|
-
/>
|
|
488
|
-
</div>
|
|
489
|
-
</template>
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
---
|
|
493
|
-
|
|
494
|
-
### Scenario G: Esportazione e Download dei Dati Filtrati
|
|
495
|
-
Il componente offre la possibilità di abilitare un pulsante per il download dei dati.
|
|
496
|
-
* Impostare `viewDownload: true` nella configurazione `DataTableConf`.
|
|
497
|
-
* Ascoltare l'evento `@ondownload`, che riceve la lista dei record attualmente filtrati dalla ricerca globale (`filterComputer`).
|
|
498
|
-
|
|
499
|
-
```html
|
|
500
|
-
<script setup lang="ts">
|
|
501
|
-
import { ref } from 'vue';
|
|
502
|
-
import DataTableList from '@/component/commons/DataTableList.vue';
|
|
503
|
-
import type { DataTableConf } from '@/model/Models';
|
|
504
|
-
|
|
505
|
-
const searchFilter = ref('');
|
|
506
|
-
|
|
507
|
-
const tableConfig = ref<DataTableConf>({
|
|
508
|
-
columns: [
|
|
509
|
-
{ title: 'ID', property: 'id' },
|
|
510
|
-
{ title: 'Nome', property: 'name' }
|
|
511
|
-
],
|
|
512
|
-
isserver: false,
|
|
513
|
-
viewActions: false,
|
|
514
|
-
viewDownload: true, // Mostra l'icona per il download in alto a destra
|
|
515
|
-
dimensionePagina: 10
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
const items = ref([
|
|
519
|
-
{ id: 1, name: 'Mario Rossi' },
|
|
520
|
-
{ id: 2, name: 'Giuseppe Verdi' },
|
|
521
|
-
{ id: 3, name: 'Luca Bianchi' }
|
|
522
|
-
]);
|
|
523
|
-
|
|
524
|
-
// Funzione di esportazione CSV client-side al click sul download
|
|
525
|
-
const handleDownload = (filteredData: any[]) => {
|
|
526
|
-
const csvHeaders = 'ID,Nome\n';
|
|
527
|
-
const csvRows = filteredData.map(item => `${item.id},"${item.name}"`).join('\n');
|
|
528
|
-
const csvContent = 'data:text/csv;charset=utf-8,' + csvHeaders + csvRows;
|
|
529
|
-
|
|
530
|
-
const encodedUri = encodeURI(csvContent);
|
|
531
|
-
const link = document.createElement('a');
|
|
532
|
-
link.setAttribute('href', encodedUri);
|
|
533
|
-
link.setAttribute('download', 'esportazione_tabella.csv');
|
|
534
|
-
document.body.appendChild(link);
|
|
535
|
-
link.click();
|
|
536
|
-
document.body.removeChild(link);
|
|
537
|
-
};
|
|
538
|
-
</script>
|
|
539
|
-
|
|
540
|
-
<template>
|
|
541
|
-
<div class="container my-4">
|
|
542
|
-
<div class="form-group mb-3">
|
|
543
|
-
<input v-model="searchFilter" type="text" class="form-control" placeholder="Cerca...">
|
|
544
|
-
</div>
|
|
545
|
-
|
|
546
|
-
<DataTableList
|
|
547
|
-
:data-items="items"
|
|
548
|
-
:data-table-conf="tableConfig"
|
|
549
|
-
:filter="searchFilter"
|
|
550
|
-
@ondownload="handleDownload"
|
|
551
|
-
/>
|
|
552
|
-
</div>
|
|
553
|
-
</template>
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
---
|
|
557
|
-
|
|
558
|
-
## 5. Dettagli Implementativi Importanti
|
|
559
|
-
|
|
560
|
-
1. **Identificativi Checkbox (`checkId`)**: Quando viene attivata la funzionalità checkbox (`showCheckbox = true`), il componente genera temporaneamente la chiave `checkId` all'interno dei record dell'array `dataItems` per identificare univocamente le righe selezionate. L'array restituito dall'evento `@onchecked` conterrà questi campi aggiuntivi.
|
|
561
|
-
2. **Reset dell'Ordinamento al cambio pagina**: Ad ogni navigazione di pagina (avanti o indietro) o reset del filtro di ricerca, viene richiamata la funzione `resetClassOrder()` che rimuove la classe CSS `.icon-primaryOrder` dall'header della colonna ordinata, indicando che la nuova pagina mostra i dati nel loro stato originario.
|