codevdesign 1.0.42 → 1.0.44
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/composants/csqcCodeBudgetaireGenerique.vue +336 -336
- package/composants/csqcDialogue.vue +118 -118
- package/composants/csqcEditeurTexteRiche.vue +380 -380
- package/composants/csqcOptionSwitch.vue +102 -102
- package/composants/csqcRecherche.vue +214 -213
- package/composants/csqcRechercheUtilisateur.vue +198 -198
- package/composants/csqcSnackbar.vue +88 -88
- package/composants/csqcTable/csqcTable.vue +537 -383
- package/composants/csqcTable/csqcTableModaleChoixColonnes.vue +831 -586
- package/composants/csqcTable/sortableDataTable.ts +24 -0
- package/composants/csqcTiroir.vue +156 -156
- package/index.ts +75 -75
- package/locales/fr.json +14 -0
- package/outils/rafraichisseurToken.ts +187 -187
- package/package.json +1 -1
|
@@ -1,586 +1,831 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<csqc-modale
|
|
3
|
-
ref="modale"
|
|
4
|
-
:titre="
|
|
5
|
-
:
|
|
6
|
-
:
|
|
7
|
-
width="
|
|
8
|
-
|
|
9
|
-
@
|
|
10
|
-
>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
v-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<csqc-modale
|
|
3
|
+
ref="modale"
|
|
4
|
+
:titre="t('csqc.csqcDatatable.choixColonnes.titre')"
|
|
5
|
+
:btn-annuler="!sauvegardeEnCours"
|
|
6
|
+
:largeur="'80vw'"
|
|
7
|
+
:max-width="'1000px'"
|
|
8
|
+
@annuler="fermer"
|
|
9
|
+
@ok="sauvegarder"
|
|
10
|
+
>
|
|
11
|
+
<!-- Erreurs -->
|
|
12
|
+
<v-alert
|
|
13
|
+
v-model="afficherErreur"
|
|
14
|
+
type="error"
|
|
15
|
+
closable
|
|
16
|
+
>
|
|
17
|
+
{{ erreur }}
|
|
18
|
+
</v-alert>
|
|
19
|
+
|
|
20
|
+
<v-row>
|
|
21
|
+
<v-col
|
|
22
|
+
cols="12"
|
|
23
|
+
class="pa-0 relative"
|
|
24
|
+
>
|
|
25
|
+
<!-- Retour en haut (table des vues) -->
|
|
26
|
+
<v-btn
|
|
27
|
+
v-show="choixRetourEnHaut"
|
|
28
|
+
class="BarreRechercheBackIcone retourHautChoix"
|
|
29
|
+
icon
|
|
30
|
+
color="primary"
|
|
31
|
+
style="position: absolute"
|
|
32
|
+
@click="retourEnHaut('#choixColonnes-choix > .v-data-table__wrapper')"
|
|
33
|
+
>
|
|
34
|
+
<v-icon>mdi-arrow-up</v-icon>
|
|
35
|
+
</v-btn>
|
|
36
|
+
|
|
37
|
+
<v-card
|
|
38
|
+
rounded
|
|
39
|
+
class="pr-0"
|
|
40
|
+
>
|
|
41
|
+
<!-- TABLE 1 : Liste des vues -->
|
|
42
|
+
<v-data-table
|
|
43
|
+
id="choixColonnes-choix"
|
|
44
|
+
class="limiteHauteurChoix"
|
|
45
|
+
:headers="colonnesChoix"
|
|
46
|
+
:items="choix"
|
|
47
|
+
:item-value="row => choixOf(row)._id"
|
|
48
|
+
v-model:expanded="expandedChoixIds"
|
|
49
|
+
show-expand
|
|
50
|
+
:single-expand="true"
|
|
51
|
+
:items-per-page="-1"
|
|
52
|
+
hide-default-footer
|
|
53
|
+
fixed-header
|
|
54
|
+
@click:row="onClickRowChoix"
|
|
55
|
+
>
|
|
56
|
+
<!-- Bouton ajouter une vue -->
|
|
57
|
+
<template #header.action>
|
|
58
|
+
<v-btn
|
|
59
|
+
color="primary"
|
|
60
|
+
class="float-right"
|
|
61
|
+
size="small"
|
|
62
|
+
:disabled="desactiverAjout"
|
|
63
|
+
@click.stop="ajouter"
|
|
64
|
+
>
|
|
65
|
+
{{ t('csqc.bouton.ajouter') }}
|
|
66
|
+
</v-btn>
|
|
67
|
+
</template>
|
|
68
|
+
<template v-slot:item.data-table-expand="{ internalItem, isExpanded, toggleExpand }">
|
|
69
|
+
<v-btn
|
|
70
|
+
:append-icon="isExpanded(internalItem) ? 'mdi-chevron-up' : 'mdi-chevron-down'"
|
|
71
|
+
:text="isExpanded(internalItem) ? 'Fermer' : 'Ouvrir'"
|
|
72
|
+
class="text-none"
|
|
73
|
+
color="medium-emphasis"
|
|
74
|
+
size="small"
|
|
75
|
+
variant="text"
|
|
76
|
+
width="105"
|
|
77
|
+
border
|
|
78
|
+
slim
|
|
79
|
+
data-expander
|
|
80
|
+
@click.stop="() => toggleExpand(internalItem)"
|
|
81
|
+
></v-btn>
|
|
82
|
+
</template>
|
|
83
|
+
<!-- Expansion : Table des colonnes -->
|
|
84
|
+
<template #expanded-row="{ columns }">
|
|
85
|
+
<tr>
|
|
86
|
+
<td
|
|
87
|
+
:colspan="columns.length"
|
|
88
|
+
class="pa-0 ma-0"
|
|
89
|
+
>
|
|
90
|
+
<v-row
|
|
91
|
+
class="pa-0 ma-0"
|
|
92
|
+
no-gutters
|
|
93
|
+
>
|
|
94
|
+
<v-col
|
|
95
|
+
cols="12"
|
|
96
|
+
class="pa-0 ma-0"
|
|
97
|
+
>
|
|
98
|
+
<!-- Retour en haut (table des colonnes) -->
|
|
99
|
+
<v-btn
|
|
100
|
+
v-show="colonnesRetourEnHaut"
|
|
101
|
+
class="BarreRechercheBackIcone retourHautChoix colonnes"
|
|
102
|
+
icon
|
|
103
|
+
color="primary"
|
|
104
|
+
style="position: absolute"
|
|
105
|
+
@click="retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')"
|
|
106
|
+
>
|
|
107
|
+
<v-icon>mdi-arrow-up</v-icon>
|
|
108
|
+
</v-btn>
|
|
109
|
+
|
|
110
|
+
<!-- TABLE 2 : Colonnes de la vue sélectionnée -->
|
|
111
|
+
<v-data-table
|
|
112
|
+
id="choixColonnes-vue"
|
|
113
|
+
class="limiteHauteurChoix colonnes mt-1 mb-4 ordonable"
|
|
114
|
+
:headers="colonnesChoixColonne"
|
|
115
|
+
:items="colonnesEnCours"
|
|
116
|
+
hover
|
|
117
|
+
item-value="value"
|
|
118
|
+
hide-default-footer
|
|
119
|
+
:items-per-page="-1"
|
|
120
|
+
fixed-header
|
|
121
|
+
:sort-by="triColonnesChoix"
|
|
122
|
+
v-sortable-data-table
|
|
123
|
+
@sorted="changeOrdre"
|
|
124
|
+
>
|
|
125
|
+
<!-- Alerte dans l'entête si aucune colonne n'est sélectionnée -->
|
|
126
|
+
<template #header.action>
|
|
127
|
+
<v-tooltip
|
|
128
|
+
v-if="nbColonnesSelectionnees <= 0"
|
|
129
|
+
location="start"
|
|
130
|
+
color="warning"
|
|
131
|
+
>
|
|
132
|
+
<template #activator="{ props }">
|
|
133
|
+
<v-icon
|
|
134
|
+
color="warning"
|
|
135
|
+
v-bind="props"
|
|
136
|
+
>
|
|
137
|
+
mdi-alert
|
|
138
|
+
</v-icon>
|
|
139
|
+
</template>
|
|
140
|
+
{{ t('csqc.csqcDatatable.choixColonnes.activezUneColonne') }}
|
|
141
|
+
</v-tooltip>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<!-- Toggle (oeil) -->
|
|
145
|
+
<template #item.action="{ item }">
|
|
146
|
+
<v-icon
|
|
147
|
+
:color="couleurColonneCliquee(colonneOf(item))"
|
|
148
|
+
@click.stop="basculeColonneClique(colonneOf(item))"
|
|
149
|
+
>
|
|
150
|
+
{{ colonneEstClique(colonneOf(item)) ? 'mdi-eye' : 'mdi-eye-off' }}
|
|
151
|
+
</v-icon>
|
|
152
|
+
</template>
|
|
153
|
+
|
|
154
|
+
<!-- Label de colonne -->
|
|
155
|
+
<template #item.title="{ value, item }">
|
|
156
|
+
{{ value ?? colonneOf(item).value }}
|
|
157
|
+
</template>
|
|
158
|
+
</v-data-table>
|
|
159
|
+
</v-col>
|
|
160
|
+
</v-row>
|
|
161
|
+
</td>
|
|
162
|
+
</tr>
|
|
163
|
+
</template>
|
|
164
|
+
|
|
165
|
+
<!-- Nom de vue (édition inline) -->
|
|
166
|
+
<template #item.nomVue="{ item }">
|
|
167
|
+
<v-text-field
|
|
168
|
+
v-if="choixOf(item).nomVue === vueEnEdition"
|
|
169
|
+
v-model.trim="nomVueEnCours"
|
|
170
|
+
:label="t('csqc.csqcDatatable.choixColonnes.nomVue')"
|
|
171
|
+
single-line
|
|
172
|
+
density="compact"
|
|
173
|
+
autofocus
|
|
174
|
+
class="py-0 my-0"
|
|
175
|
+
:rules="regles.nomVue"
|
|
176
|
+
hide-details
|
|
177
|
+
@keydown.enter="accepterEdition"
|
|
178
|
+
@keydown.esc.stop="annulerEdition"
|
|
179
|
+
/>
|
|
180
|
+
<span
|
|
181
|
+
v-else
|
|
182
|
+
:class="{ 'error--text': !choixOf(item).nomVue }"
|
|
183
|
+
>
|
|
184
|
+
{{ choixOf(item).nomVue || t('csqc.csqcDatatable.choixColonnes.nomVueRequis') }}
|
|
185
|
+
</span>
|
|
186
|
+
</template>
|
|
187
|
+
|
|
188
|
+
<!-- Actions (vue) -->
|
|
189
|
+
<template #item.action="{ item }">
|
|
190
|
+
<template v-if="choixOf(item).nomVue === vueEnEdition">
|
|
191
|
+
<v-icon
|
|
192
|
+
class="iconeSupprimer GouttiereSmall"
|
|
193
|
+
@click.stop.prevent="annulerEdition"
|
|
194
|
+
>
|
|
195
|
+
mdi-window-close
|
|
196
|
+
</v-icon>
|
|
197
|
+
|
|
198
|
+
<v-icon
|
|
199
|
+
class="iconeHover GouttiereSmall"
|
|
200
|
+
@click.stop.prevent="accepterEdition"
|
|
201
|
+
>
|
|
202
|
+
mdi-check
|
|
203
|
+
</v-icon>
|
|
204
|
+
</template>
|
|
205
|
+
|
|
206
|
+
<template v-else>
|
|
207
|
+
<v-tooltip
|
|
208
|
+
location="start"
|
|
209
|
+
color="warning"
|
|
210
|
+
>
|
|
211
|
+
<template #activator="{ props }">
|
|
212
|
+
<v-icon
|
|
213
|
+
class="iconeHover GouttiereSmall"
|
|
214
|
+
v-bind="props"
|
|
215
|
+
@click.stop.prevent="selectionner(choixOf(item))"
|
|
216
|
+
>
|
|
217
|
+
mdi-eye
|
|
218
|
+
</v-icon>
|
|
219
|
+
</template>
|
|
220
|
+
{{ t('csqc.csqcDatatable.choixColonnes.activerTooltip') }}
|
|
221
|
+
</v-tooltip>
|
|
222
|
+
<v-tooltip
|
|
223
|
+
location="start"
|
|
224
|
+
color="warning"
|
|
225
|
+
>
|
|
226
|
+
<template #activator="{ props }">
|
|
227
|
+
<v-icon
|
|
228
|
+
v-bind="props"
|
|
229
|
+
class="iconeHover GouttiereSmall"
|
|
230
|
+
:color="couleurDefaut(choixOf(item))"
|
|
231
|
+
@click.stop.prevent="mettreDefaut(choixOf(item))"
|
|
232
|
+
>
|
|
233
|
+
mdi-star
|
|
234
|
+
</v-icon>
|
|
235
|
+
</template>
|
|
236
|
+
{{ t('csqc.csqcDatatable.choixColonnes.defautTooltip') }}
|
|
237
|
+
</v-tooltip>
|
|
238
|
+
<!-- <v-icon
|
|
239
|
+
class="iconeHover GouttiereSmall"
|
|
240
|
+
@click.stop.prevent="editer(choixOf(item))"
|
|
241
|
+
>
|
|
242
|
+
mdi-pencil
|
|
243
|
+
</v-icon> -->
|
|
244
|
+
|
|
245
|
+
<v-icon
|
|
246
|
+
class="iconeSupprimer GouttiereSmall"
|
|
247
|
+
@click.stop.prevent="supprimer(choixOf(item))"
|
|
248
|
+
>
|
|
249
|
+
mdi-delete
|
|
250
|
+
</v-icon>
|
|
251
|
+
</template>
|
|
252
|
+
</template>
|
|
253
|
+
</v-data-table>
|
|
254
|
+
</v-card>
|
|
255
|
+
</v-col>
|
|
256
|
+
</v-row>
|
|
257
|
+
</csqc-modale>
|
|
258
|
+
</template>
|
|
259
|
+
|
|
260
|
+
<script setup lang="ts">
|
|
261
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* csqcTableModaleChoixColonnes.vue
|
|
265
|
+
* - Permet de créer/éditer des "vues" de colonnes (ensembles de colonnes visibles)
|
|
266
|
+
* - Drag & drop pour réordonner les colonnes
|
|
267
|
+
* - Persistance via endpoint ComposantUI/Colonnes/...
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
import axios from '../../outils/appAxios'
|
|
271
|
+
import Sortable from 'sortablejs'
|
|
272
|
+
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
|
273
|
+
import { useGoTo } from 'vuetify'
|
|
274
|
+
import { useI18n } from 'vue-i18n'
|
|
275
|
+
import CsqcModale from '../csqcDialogue.vue'
|
|
276
|
+
|
|
277
|
+
type SortableEvent = import('sortablejs').SortableEvent
|
|
278
|
+
|
|
279
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
280
|
+
// Types
|
|
281
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
282
|
+
type ColonneChoix = {
|
|
283
|
+
value: string
|
|
284
|
+
text?: string
|
|
285
|
+
ordre?: number
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
type Colonne = {
|
|
289
|
+
value: string
|
|
290
|
+
title?: string
|
|
291
|
+
ordre?: number
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
type ChoixVue = {
|
|
295
|
+
nomVue: string
|
|
296
|
+
colonnes: string[]
|
|
297
|
+
defaut: boolean
|
|
298
|
+
_id: string // interne (clé stable si nomVue est vide)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
type DataTableItemLike<T> = { raw: T }
|
|
302
|
+
|
|
303
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
304
|
+
// Helpers "unwrap" (évite item.raw dans le template + évite génériques TS dans template)
|
|
305
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
306
|
+
function asRaw<T>(item: T | DataTableItemLike<T>): T {
|
|
307
|
+
return (item as any)?.raw ?? (item as T)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const choixOf = (x: any) => asRaw<ChoixVue>(x)
|
|
311
|
+
const colonneOf = (x: any) => asRaw<Colonne>(x)
|
|
312
|
+
|
|
313
|
+
// Génère des ids pour stabiliser l’expansion / l’édition même si nomVue = ''
|
|
314
|
+
function assurerIds(vues: ChoixVue[]) {
|
|
315
|
+
for (const v of vues) {
|
|
316
|
+
if (!v._id) v._id = crypto.randomUUID()
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Clone profond simple (OK ici car données sérialisables)
|
|
321
|
+
function deepClone<T>(x: T): T {
|
|
322
|
+
return JSON.parse(JSON.stringify(x)) as T
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
326
|
+
// Props / Emits / Expose
|
|
327
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
328
|
+
type ModaleExpose = { ouvrir: () => void; fermer: () => void }
|
|
329
|
+
|
|
330
|
+
const props = defineProps<{
|
|
331
|
+
urlbase: string
|
|
332
|
+
formulaireId: number
|
|
333
|
+
identifiant: string
|
|
334
|
+
colonnes: ColonneChoix[]
|
|
335
|
+
choixOrigine: ChoixVue[]
|
|
336
|
+
}>()
|
|
337
|
+
|
|
338
|
+
const emit = defineEmits<{
|
|
339
|
+
(e: 'selection', choix: ChoixVue): void
|
|
340
|
+
(e: 'sauvegarde', choix: ChoixVue[]): void
|
|
341
|
+
}>()
|
|
342
|
+
|
|
343
|
+
const modale = ref<ModaleExpose | null>(null)
|
|
344
|
+
|
|
345
|
+
defineExpose({ ouvrir, fermer })
|
|
346
|
+
|
|
347
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
348
|
+
// Directive: drag & drop (sortablejs)
|
|
349
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
350
|
+
defineOptions({
|
|
351
|
+
directives: {
|
|
352
|
+
sortableDataTable: {
|
|
353
|
+
mounted(el: HTMLElement, _binding: any, vnode: any) {
|
|
354
|
+
const tbody = el.getElementsByTagName('tbody')?.[0]
|
|
355
|
+
if (!tbody) return
|
|
356
|
+
|
|
357
|
+
Sortable.create(tbody, {
|
|
358
|
+
animation: 150,
|
|
359
|
+
onUpdate(event: SortableEvent) {
|
|
360
|
+
if (event.oldIndex == null || event.newIndex == null) return
|
|
361
|
+
vnode?.component?.emit?.('sorted', event)
|
|
362
|
+
},
|
|
363
|
+
})
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
370
|
+
// UI / i18n / scroll helpers
|
|
371
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
372
|
+
const { t } = useI18n()
|
|
373
|
+
const goTo = useGoTo()
|
|
374
|
+
|
|
375
|
+
const erreur = ref('')
|
|
376
|
+
const afficherErreur = ref(false)
|
|
377
|
+
|
|
378
|
+
const choixRetourEnHaut = ref(false)
|
|
379
|
+
const colonnesRetourEnHaut = ref(false)
|
|
380
|
+
|
|
381
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
382
|
+
// State
|
|
383
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
384
|
+
const choix = ref<ChoixVue[]>([])
|
|
385
|
+
const choixEnCours = ref<ChoixVue | null>(null)
|
|
386
|
+
const colonnesEnCours = ref<Colonne[]>([])
|
|
387
|
+
|
|
388
|
+
const sauvegardeEnCours = ref(false)
|
|
389
|
+
|
|
390
|
+
// Edition du nom de vue
|
|
391
|
+
const nomVueEnCours = ref('')
|
|
392
|
+
const vueEnEdition = ref('')
|
|
393
|
+
const desactiverAjout = ref(false)
|
|
394
|
+
|
|
395
|
+
// Tri visuel (on veut ordre asc)
|
|
396
|
+
const triColonnesChoix = ref<Array<{ key: string; order?: 'asc' | 'desc' }>>([{ key: 'ordre', order: 'asc' }])
|
|
397
|
+
|
|
398
|
+
// Règles validation
|
|
399
|
+
const regles = reactive({
|
|
400
|
+
nomVue: [
|
|
401
|
+
(v: string) => (v && v.trim().length >= 1) || t('csqc.csqcDatatable.choixColonnes.nomVueRequis'),
|
|
402
|
+
(v: string) =>
|
|
403
|
+
!choix.value.some(({ nomVue }) => v.trim() === nomVue) || t('csqc.csqcDatatable.choixColonnes.nomVueExiste'),
|
|
404
|
+
],
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
// Affiche l'alerte si aucune colonne sélectionnée dans la vue courante
|
|
408
|
+
const nbColonnesSelectionnees = computed(() => choixEnCours.value?.colonnes?.length ?? 0)
|
|
409
|
+
|
|
410
|
+
function ouvrirVue(ch: ChoixVue) {
|
|
411
|
+
// remonte en haut la table des colonnes quand on change
|
|
412
|
+
if (choixEnCours.value) retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')
|
|
413
|
+
|
|
414
|
+
// Build colonnesEnCours à partir des colonnes disponibles
|
|
415
|
+
colonnesEnCours.value = deepClone(props.colonnes)
|
|
416
|
+
|
|
417
|
+
// Référence réactive dans la liste
|
|
418
|
+
const refDansListe = choix.value.find(x => x._id === ch._id) ?? ch
|
|
419
|
+
choixEnCours.value = refDansListe
|
|
420
|
+
|
|
421
|
+
// Colonnes sélectionnées d'abord, puis le reste
|
|
422
|
+
const selected: Colonne[] = []
|
|
423
|
+
const missing: string[] = []
|
|
424
|
+
|
|
425
|
+
for (const key of choixEnCours.value.colonnes) {
|
|
426
|
+
const col = colonnesEnCours.value.find(c => c.value === key)
|
|
427
|
+
if (!col) missing.push(key)
|
|
428
|
+
else selected.push(col)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Nettoyage des colonnes disparues
|
|
432
|
+
if (missing.length) {
|
|
433
|
+
choixEnCours.value.colonnes = choixEnCours.value.colonnes.filter(k => !missing.includes(k))
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// selected puis disponibles non sélectionnées
|
|
437
|
+
colonnesEnCours.value = selected.concat(
|
|
438
|
+
colonnesEnCours.value.filter(c => !choixEnCours.value!.colonnes.includes(c.value)),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
calculOrdreColonnes()
|
|
442
|
+
ecouteDefilerColonnes()
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const expandedChoixIds = ref<string[]>([])
|
|
446
|
+
|
|
447
|
+
// colonnes
|
|
448
|
+
const colonnesChoixColonne = computed(() => [
|
|
449
|
+
{
|
|
450
|
+
title: t('csqc.csqcDatatable.choixColonnes.ordre'),
|
|
451
|
+
key: 'ordre',
|
|
452
|
+
align: 'start' as const,
|
|
453
|
+
sortable: false,
|
|
454
|
+
width: '5%',
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
title: t('csqc.csqcDatatable.choixColonnes.nomColonne', colonnesEnCours.value.length),
|
|
458
|
+
key: 'title',
|
|
459
|
+
align: 'start' as const,
|
|
460
|
+
sortable: false,
|
|
461
|
+
width: '85%',
|
|
462
|
+
},
|
|
463
|
+
{ title: '', key: 'action', align: 'end' as const, sortable: false, width: '10%' },
|
|
464
|
+
])
|
|
465
|
+
|
|
466
|
+
const colonnesChoix = computed(() => [
|
|
467
|
+
{
|
|
468
|
+
title: t('csqc.csqcDatatable.choixColonnes.vues', choix.value.length),
|
|
469
|
+
key: 'nomVue',
|
|
470
|
+
width: '70%',
|
|
471
|
+
align: 'start' as const,
|
|
472
|
+
},
|
|
473
|
+
{ title: '', key: 'action', width: '30%', align: 'end' as const, sortable: false },
|
|
474
|
+
])
|
|
475
|
+
|
|
476
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
477
|
+
// actions
|
|
478
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
479
|
+
function ouvrir() {
|
|
480
|
+
choix.value = deepClone(props.choixOrigine)
|
|
481
|
+
assurerIds(choix.value) // obligatoire
|
|
482
|
+
expandedChoixIds.value = [] // reset
|
|
483
|
+
modale.value?.ouvrir()
|
|
484
|
+
}
|
|
485
|
+
function fermer() {
|
|
486
|
+
// Reset UI
|
|
487
|
+
sauvegardeEnCours.value = false
|
|
488
|
+
desactiverAjout.value = false
|
|
489
|
+
vueEnEdition.value = ''
|
|
490
|
+
nomVueEnCours.value = ''
|
|
491
|
+
|
|
492
|
+
// Reset sélection
|
|
493
|
+
choixEnCours.value = null
|
|
494
|
+
colonnesEnCours.value = []
|
|
495
|
+
|
|
496
|
+
// Reset erreurs
|
|
497
|
+
afficherErreur.value = false
|
|
498
|
+
erreur.value = ''
|
|
499
|
+
|
|
500
|
+
modale.value?.fermer()
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
504
|
+
// Actions : vues
|
|
505
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
506
|
+
function ajouter() {
|
|
507
|
+
const ch: ChoixVue = {
|
|
508
|
+
_id: crypto.randomUUID(),
|
|
509
|
+
nomVue: '',
|
|
510
|
+
colonnes: [],
|
|
511
|
+
defaut: choix.value.length === 0,
|
|
512
|
+
}
|
|
513
|
+
choix.value.push(ch)
|
|
514
|
+
expandedChoixIds.value = [ch._id] // ouvre celle-là
|
|
515
|
+
editer(ch)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function editer(ch: ChoixVue) {
|
|
519
|
+
desactiverAjout.value = true
|
|
520
|
+
|
|
521
|
+
// Nettoyage d’un placeholder vide précédent (si on relance edit)
|
|
522
|
+
if (ch.nomVue !== '') supprimer({ _id: 'placeholder', nomVue: '', colonnes: [], defaut: false })
|
|
523
|
+
|
|
524
|
+
erreur.value = ''
|
|
525
|
+
nomVueEnCours.value = ch.nomVue
|
|
526
|
+
vueEnEdition.value = ch.nomVue
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function accepterEdition() {
|
|
530
|
+
const nom = nomVueEnCours.value?.trim()
|
|
531
|
+
|
|
532
|
+
if (!nom) {
|
|
533
|
+
erreur.value = t('csqc.csqcDatatable.choixColonnes.nomVueRequis')
|
|
534
|
+
return
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (choix.value.some(({ nomVue }) => nomVue === nom)) {
|
|
538
|
+
erreur.value = t('csqc.csqcDatatable.choixColonnes.nomVueExiste')
|
|
539
|
+
return
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const ch = choix.value.find(({ nomVue }) => nomVue === vueEnEdition.value)
|
|
543
|
+
if (!ch) return
|
|
544
|
+
|
|
545
|
+
const etaitSelectionnee = choixEstClique(ch)
|
|
546
|
+
ch.nomVue = nom
|
|
547
|
+
|
|
548
|
+
// Si c’était la vue active, on s’assure que la ref active reste valide
|
|
549
|
+
if (etaitSelectionnee) {
|
|
550
|
+
choixEnCours.value = ch
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Reset édition
|
|
554
|
+
vueEnEdition.value = ''
|
|
555
|
+
nomVueEnCours.value = ''
|
|
556
|
+
desactiverAjout.value = false
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function annulerEdition() {
|
|
560
|
+
const ch = choix.value.find(({ nomVue }) => nomVue === vueEnEdition.value)
|
|
561
|
+
if (!ch) return
|
|
562
|
+
|
|
563
|
+
// Si c'était une nouvelle vue vide, on la retire
|
|
564
|
+
if (ch.nomVue === '') supprimer(ch)
|
|
565
|
+
|
|
566
|
+
vueEnEdition.value = ''
|
|
567
|
+
nomVueEnCours.value = ''
|
|
568
|
+
desactiverAjout.value = false
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function selectionner(ch: ChoixVue) {
|
|
572
|
+
emit('selection', ch)
|
|
573
|
+
fermer()
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function supprimer(ch: ChoixVue) {
|
|
577
|
+
if (choixEstClique(ch)) choixEnCours.value = null
|
|
578
|
+
|
|
579
|
+
const index = choix.value.findIndex(({ nomVue }) => nomVue === ch.nomVue)
|
|
580
|
+
if (index === -1) return
|
|
581
|
+
|
|
582
|
+
// Si on supprime le défaut, on transfère le défaut ailleurs
|
|
583
|
+
if (choix.value[index].defaut) {
|
|
584
|
+
const autre = choix.value.find(({ nomVue }) => nomVue !== ch.nomVue)
|
|
585
|
+
if (autre) autre.defaut = true
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
choix.value.splice(index, 1)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function mettreDefaut(ch: ChoixVue) {
|
|
592
|
+
const actuel = choix.value.find(v => v.defaut)
|
|
593
|
+
if (actuel) actuel.defaut = false
|
|
594
|
+
|
|
595
|
+
const cible = choix.value.find(v => v.nomVue === ch.nomVue)
|
|
596
|
+
if (cible) cible.defaut = true
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function couleurDefaut(ch: ChoixVue) {
|
|
600
|
+
return ch.defaut ? '#daa520' : 'gray'
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function choixEstClique(ch: ChoixVue) {
|
|
604
|
+
return ch.nomVue === choixEnCours.value?.nomVue
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Click row Vuetify (payload = { item })
|
|
608
|
+
function onClickRowChoix(_e: Event, payload: any) {
|
|
609
|
+
cliquer(choixOf(payload?.item ?? payload))
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Sélection d’une vue :
|
|
614
|
+
* - recalcul de colonnesEnCours (selected d'abord, puis le reste)
|
|
615
|
+
* - nettoyage des colonnes "non disponibles" (si props.colonnes a changé)
|
|
616
|
+
*/
|
|
617
|
+
function cliquer(ch: ChoixVue) {
|
|
618
|
+
const id = ch._id
|
|
619
|
+
if (!id) return
|
|
620
|
+
|
|
621
|
+
expandedChoixIds.value = expandedChoixIds.value[0] === id ? [] : [id]
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
625
|
+
// Actions : colonnes
|
|
626
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
627
|
+
function colonneEstClique(colonne: Colonne) {
|
|
628
|
+
if (!choixEnCours.value) return false
|
|
629
|
+
return choixEnCours.value.colonnes.includes(colonne.value)
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function couleurColonneCliquee(colonne: Colonne) {
|
|
633
|
+
return colonneEstClique(colonne) ? 'primary' : 'gray'
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function basculeColonneClique(colonne: Colonne) {
|
|
637
|
+
if (!choixEnCours.value) return
|
|
638
|
+
|
|
639
|
+
if (colonneEstClique(colonne)) {
|
|
640
|
+
choixEnCours.value.colonnes = choixEnCours.value.colonnes.filter(x => x !== colonne.value)
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
choixEnCours.value.colonnes.push(colonne.value)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Liste des colonnes actuellement "cliquées" selon l’ordre affiché
|
|
648
|
+
const colonnesCliquees = computed(() => {
|
|
649
|
+
if (!choixEnCours.value) return []
|
|
650
|
+
return colonnesEnCours.value.filter(c => choixEnCours.value!.colonnes.includes(c.value)).map(c => c.value)
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
function changeOrdre(event: SortableEvent) {
|
|
654
|
+
if (event.oldIndex == null || event.newIndex == null) return
|
|
655
|
+
|
|
656
|
+
const moved = colonnesEnCours.value.splice(event.oldIndex, 1)[0]
|
|
657
|
+
colonnesEnCours.value.splice(event.newIndex, 0, moved)
|
|
658
|
+
|
|
659
|
+
calculOrdreColonnes()
|
|
660
|
+
|
|
661
|
+
// Après reorder, on sauvegarde la sélection dans le nouvel ordre visuel
|
|
662
|
+
if (choixEnCours.value) {
|
|
663
|
+
choixEnCours.value.colonnes = colonnesCliquees.value
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function calculOrdreColonnes() {
|
|
668
|
+
for (let i = 0; i < colonnesEnCours.value.length; i += 1) {
|
|
669
|
+
colonnesEnCours.value[i].ordre = i + 1
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
674
|
+
// axios
|
|
675
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
676
|
+
async function sauvegarder() {
|
|
677
|
+
sauvegardeEnCours.value = true
|
|
678
|
+
|
|
679
|
+
try {
|
|
680
|
+
const res: any = await axios
|
|
681
|
+
.getAxios()
|
|
682
|
+
.post(`${props.urlbase}/api/ComposantUI/Colonnes/${props.formulaireId}/Identifiant/${props.identifiant}`, {
|
|
683
|
+
valeur: JSON.stringify(choix.value),
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
const payload = res?.data ?? res
|
|
687
|
+
|
|
688
|
+
// payload peut être un json string ou déjà un tableau
|
|
689
|
+
choix.value = typeof payload === 'string' ? (JSON.parse(payload) as ChoixVue[]) : (payload as ChoixVue[])
|
|
690
|
+
|
|
691
|
+
assurerIds(choix.value)
|
|
692
|
+
|
|
693
|
+
// Notifie le parent
|
|
694
|
+
emit('sauvegarde', choix.value)
|
|
695
|
+
if (choixEnCours.value) emit('selection', choixEnCours.value)
|
|
696
|
+
|
|
697
|
+
fermer()
|
|
698
|
+
} catch (e) {
|
|
699
|
+
erreur.value = String(e)
|
|
700
|
+
} finally {
|
|
701
|
+
sauvegardeEnCours.value = false
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
706
|
+
// Scroll helpers
|
|
707
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
708
|
+
function retourEnHaut(cible: string) {
|
|
709
|
+
goTo(0, { container: cible })
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function defilerChoix(e: Event) {
|
|
713
|
+
const target = e.target as HTMLElement
|
|
714
|
+
choixRetourEnHaut.value = target.scrollTop >= 200
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function ecouteDefilerChoix() {
|
|
718
|
+
const el = document.querySelector('#choixColonnes-choix > .v-data-table__wrapper')
|
|
719
|
+
if (!el) {
|
|
720
|
+
setTimeout(ecouteDefilerChoix, 100)
|
|
721
|
+
return
|
|
722
|
+
}
|
|
723
|
+
el.addEventListener('scroll', defilerChoix)
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function defilerColonnes(e: Event) {
|
|
727
|
+
const target = e.target as HTMLElement
|
|
728
|
+
colonnesRetourEnHaut.value = target.scrollTop >= 200
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function ecouteDefilerColonnes() {
|
|
732
|
+
nextTick(() => {
|
|
733
|
+
const el = document.querySelector('#choixColonnes-vue > .v-data-table__wrapper')
|
|
734
|
+
if (!el) {
|
|
735
|
+
setTimeout(ecouteDefilerColonnes, 100)
|
|
736
|
+
return
|
|
737
|
+
}
|
|
738
|
+
el.addEventListener('scroll', defilerColonnes)
|
|
739
|
+
})
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
743
|
+
// Watchers
|
|
744
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
745
|
+
watch(erreur, () => {
|
|
746
|
+
afficherErreur.value = erreur.value !== ''
|
|
747
|
+
})
|
|
748
|
+
|
|
749
|
+
watch(afficherErreur, v => {
|
|
750
|
+
if (v === false) erreur.value = ''
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
watch(
|
|
754
|
+
expandedChoixIds,
|
|
755
|
+
v => {
|
|
756
|
+
const id = v?.[0]
|
|
757
|
+
|
|
758
|
+
// Fermeture
|
|
759
|
+
if (!id) {
|
|
760
|
+
choixEnCours.value = null
|
|
761
|
+
colonnesEnCours.value = []
|
|
762
|
+
return
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Ouverture / changement
|
|
766
|
+
const ch = choix.value.find(x => x._id === id)
|
|
767
|
+
if (!ch) return
|
|
768
|
+
|
|
769
|
+
ouvrirVue(ch) // IMPORTANT: ouverture "force", pas toggle
|
|
770
|
+
},
|
|
771
|
+
{ flush: 'post' }, // <-- clé du fix
|
|
772
|
+
)
|
|
773
|
+
watch(
|
|
774
|
+
expandedChoixIds,
|
|
775
|
+
v => {
|
|
776
|
+
if (v.length > 1) expandedChoixIds.value = [v[v.length - 1]] // garde le dernier
|
|
777
|
+
},
|
|
778
|
+
{ flush: 'sync' },
|
|
779
|
+
)
|
|
780
|
+
</script>
|
|
781
|
+
|
|
782
|
+
<style scoped>
|
|
783
|
+
/* Layout */
|
|
784
|
+
.relative {
|
|
785
|
+
position: relative;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* Boutons "retour en haut" */
|
|
789
|
+
.retourHautChoix {
|
|
790
|
+
bottom: 4px !important;
|
|
791
|
+
right: 4px;
|
|
792
|
+
}
|
|
793
|
+
.retourHautChoix.colonnes {
|
|
794
|
+
bottom: 8px !important;
|
|
795
|
+
right: 48px;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/* Scroll vertical des tables */
|
|
799
|
+
.limiteHauteurChoix > .v-data-table__wrapper {
|
|
800
|
+
max-height: min(80vh - 200px, 900px);
|
|
801
|
+
overflow-y: auto;
|
|
802
|
+
}
|
|
803
|
+
.limiteHauteurChoix.colonnes > .v-data-table__wrapper {
|
|
804
|
+
max-height: min(80vh - 294px, 600px);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/* Z-index du header table 1 */
|
|
808
|
+
#choixColonnes-choix > .v-data-table__wrapper > table > thead > tr > th {
|
|
809
|
+
z-index: 3;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* Drag cursor */
|
|
813
|
+
.v-data-table.ordonable > .v-data-table__wrapper > table > tbody > tr {
|
|
814
|
+
cursor: move;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/* Icons */
|
|
818
|
+
.iconeSupprimer:hover {
|
|
819
|
+
color: red;
|
|
820
|
+
}
|
|
821
|
+
.v-icon.v-icon.v-icon--link.iconeSupprimer:hover {
|
|
822
|
+
color: red !important;
|
|
823
|
+
}
|
|
824
|
+
.iconeEditer:hover {
|
|
825
|
+
color: #095797;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.GouttiereSmall {
|
|
829
|
+
margin-left: 8px;
|
|
830
|
+
}
|
|
831
|
+
</style>
|