codevdesign 1.0.43 → 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/csqcDialogue.vue +118 -118
- package/composants/csqcTable/csqcTable.vue +223 -70
- package/composants/csqcTable/csqcTableModaleChoixColonnes.vue +728 -483
- package/composants/csqcTable/sortableDataTable.ts +24 -0
- package/composants/csqcTiroir.vue +156 -156
- package/locales/fr.json +14 -0
- package/package.json +2 -2
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-dialog
|
|
3
|
-
v-model="dialog"
|
|
4
|
-
v-bind="$attrs"
|
|
5
|
-
class="pa-0 ma-0"
|
|
6
|
-
:width="largeur"
|
|
7
|
-
:fullscreen="display.xs.value"
|
|
8
|
-
max-width="650"
|
|
9
|
-
:persistent="props.persistant"
|
|
10
|
-
@keydown.esc="!persistant ? annuler : ''"
|
|
11
|
-
@click:outside="!persistant ? annuler : ''"
|
|
12
|
-
>
|
|
13
|
-
<v-card class="pa-0 ma-0 pl-8 pt-8">
|
|
14
|
-
<!-- Bouton en haut à droite -->
|
|
15
|
-
<v-btn
|
|
16
|
-
icon="mdi-close"
|
|
17
|
-
variant="text"
|
|
18
|
-
class="position-absolute
|
|
19
|
-
style="top: 5px; right: 5px"
|
|
20
|
-
@click="annuler"
|
|
21
|
-
></v-btn>
|
|
22
|
-
|
|
23
|
-
<v-card-title
|
|
24
|
-
class="pa-0 ma-0 pb-6"
|
|
25
|
-
style="font-size: 24px; white-space: normal; word-break: break-word"
|
|
26
|
-
>
|
|
27
|
-
<slot name="titre"></slot>
|
|
28
|
-
<div text-h5><span v-html="titre"></span></div>
|
|
29
|
-
</v-card-title>
|
|
30
|
-
|
|
31
|
-
<v-card-text class="pa-0 ma-0 pb-6 pr-6">
|
|
32
|
-
<v-container>
|
|
33
|
-
<slot></slot>
|
|
34
|
-
<slot name="content"></slot>
|
|
35
|
-
</v-container>
|
|
36
|
-
</v-card-text>
|
|
37
|
-
<v-card-actions class="my-2 d-flex justify-end pr-6 pb-5">
|
|
38
|
-
<slot name="actions"></slot>
|
|
39
|
-
<v-btn
|
|
40
|
-
v-if="btnAnnuler"
|
|
41
|
-
color="primary"
|
|
42
|
-
:loading="operationEnCours"
|
|
43
|
-
variant="text"
|
|
44
|
-
@click="annuler"
|
|
45
|
-
>
|
|
46
|
-
{{ btnAnnulerTexte ? btnAnnulerTexte : $t('csqc.bouton.annuler') }}
|
|
47
|
-
</v-btn>
|
|
48
|
-
|
|
49
|
-
<v-btn
|
|
50
|
-
v-if="btnOk"
|
|
51
|
-
class="Gouttiere"
|
|
52
|
-
color="primary"
|
|
53
|
-
:loading="operationEnCours"
|
|
54
|
-
:disabled="btnOkDesactiver"
|
|
55
|
-
variant="flat"
|
|
56
|
-
@click="okBouton"
|
|
57
|
-
>
|
|
58
|
-
{{ btnOkTexte ? btnOkTexte : $t('csqc.bouton.ok') }}
|
|
59
|
-
</v-btn>
|
|
60
|
-
</v-card-actions>
|
|
61
|
-
</v-card>
|
|
62
|
-
</v-dialog>
|
|
63
|
-
</template>
|
|
64
|
-
|
|
65
|
-
<script setup lang="ts">
|
|
66
|
-
import { ref } from 'vue'
|
|
67
|
-
import { useDisplay } from 'vuetify'
|
|
68
|
-
|
|
69
|
-
const display = useDisplay()
|
|
70
|
-
|
|
71
|
-
// Déclaration des props
|
|
72
|
-
const props = defineProps({
|
|
73
|
-
largeur: { type: String, default: '50vw' },
|
|
74
|
-
persistant: { type: Boolean, default: true },
|
|
75
|
-
operationEnCours: { type: Boolean, default: false },
|
|
76
|
-
btnAnnuler: { type: Boolean, default: true },
|
|
77
|
-
btnOk: { type: Boolean, default: true },
|
|
78
|
-
btnAnnulerTexte: { type: String, default: '' },
|
|
79
|
-
btnOkTexte: { type: String, default: '' },
|
|
80
|
-
titre: { type: String, default: '' },
|
|
81
|
-
btnOkDesactiver: { type: Boolean, default: false },
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
// Déclaration des événements émis
|
|
85
|
-
const emit = defineEmits(['annuler', 'ok'])
|
|
86
|
-
|
|
87
|
-
// Gestion de l'état du dialogue
|
|
88
|
-
const dialog = ref(false)
|
|
89
|
-
|
|
90
|
-
// Méthodes pour gérer l'ouverture et la fermeture
|
|
91
|
-
const ouvrir = () => {
|
|
92
|
-
dialog.value = true
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const fermer = () => {
|
|
96
|
-
dialog.value = false
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Gestion des actions des boutons
|
|
100
|
-
const annuler = () => {
|
|
101
|
-
emit('annuler')
|
|
102
|
-
fermer()
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const okBouton = () => {
|
|
106
|
-
emit('ok')
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// permet d'exporter les 2 actions
|
|
110
|
-
defineExpose({ ouvrir, fermer })
|
|
111
|
-
</script>
|
|
112
|
-
|
|
113
|
-
<style lang="css" scoped>
|
|
114
|
-
.v-card__text,
|
|
115
|
-
.v-card__title {
|
|
116
|
-
word-break: normal; /* empeche le wrap de couper un mot en XS */
|
|
117
|
-
}
|
|
118
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog
|
|
3
|
+
v-model="dialog"
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
class="pa-0 ma-0"
|
|
6
|
+
:width="largeur"
|
|
7
|
+
:fullscreen="display.xs.value"
|
|
8
|
+
max-width="650"
|
|
9
|
+
:persistent="props.persistant"
|
|
10
|
+
@keydown.esc="!persistant ? annuler : ''"
|
|
11
|
+
@click:outside="!persistant ? annuler : ''"
|
|
12
|
+
>
|
|
13
|
+
<v-card class="pa-0 ma-0 pl-8 pt-8">
|
|
14
|
+
<!-- Bouton en haut à droite -->
|
|
15
|
+
<v-btn
|
|
16
|
+
icon="mdi-close"
|
|
17
|
+
variant="text"
|
|
18
|
+
class="position-absolute iconeHover"
|
|
19
|
+
style="top: 5px; right: 5px"
|
|
20
|
+
@click="annuler"
|
|
21
|
+
></v-btn>
|
|
22
|
+
|
|
23
|
+
<v-card-title
|
|
24
|
+
class="pa-0 ma-0 pb-6"
|
|
25
|
+
style="font-size: 24px; white-space: normal; word-break: break-word"
|
|
26
|
+
>
|
|
27
|
+
<slot name="titre"></slot>
|
|
28
|
+
<div text-h5><span v-html="titre"></span></div>
|
|
29
|
+
</v-card-title>
|
|
30
|
+
|
|
31
|
+
<v-card-text class="pa-0 ma-0 pb-6 pr-6">
|
|
32
|
+
<v-container>
|
|
33
|
+
<slot></slot>
|
|
34
|
+
<slot name="content"></slot>
|
|
35
|
+
</v-container>
|
|
36
|
+
</v-card-text>
|
|
37
|
+
<v-card-actions class="my-2 d-flex justify-end pr-6 pb-5">
|
|
38
|
+
<slot name="actions"></slot>
|
|
39
|
+
<v-btn
|
|
40
|
+
v-if="btnAnnuler"
|
|
41
|
+
color="primary"
|
|
42
|
+
:loading="operationEnCours"
|
|
43
|
+
variant="text"
|
|
44
|
+
@click="annuler"
|
|
45
|
+
>
|
|
46
|
+
{{ btnAnnulerTexte ? btnAnnulerTexte : $t('csqc.bouton.annuler') }}
|
|
47
|
+
</v-btn>
|
|
48
|
+
|
|
49
|
+
<v-btn
|
|
50
|
+
v-if="btnOk"
|
|
51
|
+
class="Gouttiere"
|
|
52
|
+
color="primary"
|
|
53
|
+
:loading="operationEnCours"
|
|
54
|
+
:disabled="btnOkDesactiver"
|
|
55
|
+
variant="flat"
|
|
56
|
+
@click="okBouton"
|
|
57
|
+
>
|
|
58
|
+
{{ btnOkTexte ? btnOkTexte : $t('csqc.bouton.ok') }}
|
|
59
|
+
</v-btn>
|
|
60
|
+
</v-card-actions>
|
|
61
|
+
</v-card>
|
|
62
|
+
</v-dialog>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<script setup lang="ts">
|
|
66
|
+
import { ref } from 'vue'
|
|
67
|
+
import { useDisplay } from 'vuetify'
|
|
68
|
+
|
|
69
|
+
const display = useDisplay()
|
|
70
|
+
|
|
71
|
+
// Déclaration des props
|
|
72
|
+
const props = defineProps({
|
|
73
|
+
largeur: { type: String, default: '50vw' },
|
|
74
|
+
persistant: { type: Boolean, default: true },
|
|
75
|
+
operationEnCours: { type: Boolean, default: false },
|
|
76
|
+
btnAnnuler: { type: Boolean, default: true },
|
|
77
|
+
btnOk: { type: Boolean, default: true },
|
|
78
|
+
btnAnnulerTexte: { type: String, default: '' },
|
|
79
|
+
btnOkTexte: { type: String, default: '' },
|
|
80
|
+
titre: { type: String, default: '' },
|
|
81
|
+
btnOkDesactiver: { type: Boolean, default: false },
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Déclaration des événements émis
|
|
85
|
+
const emit = defineEmits(['annuler', 'ok'])
|
|
86
|
+
|
|
87
|
+
// Gestion de l'état du dialogue
|
|
88
|
+
const dialog = ref(false)
|
|
89
|
+
|
|
90
|
+
// Méthodes pour gérer l'ouverture et la fermeture
|
|
91
|
+
const ouvrir = () => {
|
|
92
|
+
dialog.value = true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const fermer = () => {
|
|
96
|
+
dialog.value = false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Gestion des actions des boutons
|
|
100
|
+
const annuler = () => {
|
|
101
|
+
emit('annuler')
|
|
102
|
+
fermer()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const okBouton = () => {
|
|
106
|
+
emit('ok')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// permet d'exporter les 2 actions
|
|
110
|
+
defineExpose({ ouvrir, fermer })
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<style lang="css" scoped>
|
|
114
|
+
.v-card__text,
|
|
115
|
+
.v-card__title {
|
|
116
|
+
word-break: normal; /* empeche le wrap de couper un mot en XS */
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
:nom-fichier="excelNomFichier"
|
|
42
42
|
class="mt-1 ml-1 float-right"
|
|
43
43
|
/>
|
|
44
|
+
<v-icon
|
|
45
|
+
v-if="permettreChoixColonnes"
|
|
46
|
+
color="grisMoyen"
|
|
47
|
+
class="mt-1 ml-1 float-right"
|
|
48
|
+
@click.stop="ouvrirChoixColonnes"
|
|
49
|
+
>
|
|
50
|
+
mdi-table-edit
|
|
51
|
+
</v-icon>
|
|
44
52
|
</template>
|
|
45
53
|
<template #rechercheAvanceeTitre>
|
|
46
54
|
<slot name="rechercheAvanceeTitre" />
|
|
@@ -64,7 +72,7 @@
|
|
|
64
72
|
<v-data-table
|
|
65
73
|
ref="datatable"
|
|
66
74
|
v-bind="$attrs"
|
|
67
|
-
:headers="
|
|
75
|
+
:headers="colonnesAffichees"
|
|
68
76
|
:item-key="itemKey"
|
|
69
77
|
:items="liste"
|
|
70
78
|
:search="recherche"
|
|
@@ -123,166 +131,311 @@
|
|
|
123
131
|
:largeur="modaleSupprimerLargeur"
|
|
124
132
|
@confirmer="supprimer"
|
|
125
133
|
/>
|
|
134
|
+
<modale-choix
|
|
135
|
+
ref="modaleChoix"
|
|
136
|
+
v-if="permettreChoixColonnes"
|
|
137
|
+
:urlbase="urlbase"
|
|
138
|
+
:formulaire-id="formulaireId"
|
|
139
|
+
:identifiant="identifiant"
|
|
140
|
+
:colonnes="colonnesPourChoix"
|
|
141
|
+
:choix-origine="choixOrigineNormalise"
|
|
142
|
+
@selection="selectionChoix"
|
|
143
|
+
@sauvegarde="sauvegardeChoix"
|
|
144
|
+
/>
|
|
126
145
|
</v-col>
|
|
127
146
|
</v-row>
|
|
128
147
|
</div>
|
|
129
148
|
</template>
|
|
130
|
-
|
|
131
149
|
<script setup lang="ts">
|
|
132
150
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
151
|
+
import { computed, onMounted, ref, type PropType, type Slots } from 'vue'
|
|
152
|
+
import { useI18n } from 'vue-i18n'
|
|
153
|
+
import type { SortItem } from 'vuetify/lib/components/VDataTable/composables/sort.mjs'
|
|
133
154
|
|
|
134
|
-
import { ref, computed, type Slots, type PropType } from 'vue'
|
|
135
155
|
import Recherche from '../csqcRecherche.vue'
|
|
136
156
|
import confirmation from '../csqcConfirmation.vue'
|
|
137
157
|
import exportExcelComponent from './csqcTableExportExcel.vue'
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
import { useI18n } from 'vue-i18n'
|
|
158
|
+
import ModaleChoix from './csqcTableModaleChoixColonnes.vue'
|
|
159
|
+
import axios from '../../outils/appAxios'
|
|
141
160
|
import Colonne from '../../modeles/composants/datatableColonne'
|
|
142
|
-
import type { SortItem } from 'vuetify/lib/components/VDataTable/composables/sort.mjs'
|
|
143
161
|
|
|
162
|
+
type VueColonnes = {
|
|
163
|
+
nomVue: string
|
|
164
|
+
colonnes: string[]
|
|
165
|
+
defaut?: boolean
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const slots = defineSlots<Slots>()
|
|
169
|
+
const emit = defineEmits(['ajouter', 'cliqueLigne', 'supprimer', 'modifier', 'donneesExportees', 'panneau:etat'])
|
|
170
|
+
|
|
171
|
+
// ============================================================================
|
|
144
172
|
// Props
|
|
173
|
+
// ============================================================================
|
|
145
174
|
const props = defineProps({
|
|
146
175
|
barreHautAfficher: { type: Boolean, default: true },
|
|
176
|
+
|
|
147
177
|
btnAjouter: { type: Boolean, default: true },
|
|
148
178
|
btnAjouterTexte: { type: String, default: '' },
|
|
149
179
|
btnModifier: { type: Boolean, default: true },
|
|
150
180
|
btnSupprimer: { type: Boolean, default: true },
|
|
181
|
+
|
|
151
182
|
chargementListe: { type: Boolean, default: false },
|
|
152
183
|
operationEnCours: { type: Boolean, default: false },
|
|
153
|
-
|
|
154
|
-
//
|
|
184
|
+
|
|
185
|
+
// Headers Vuetify
|
|
155
186
|
colonnes: {
|
|
156
187
|
type: Array as PropType<Colonne[]>,
|
|
157
188
|
default: () => [],
|
|
158
189
|
},
|
|
190
|
+
|
|
159
191
|
densite: { type: String as PropType<'default' | 'comfortable' | 'compact'>, default: 'default' },
|
|
192
|
+
|
|
160
193
|
excel: { type: Boolean, default: false },
|
|
161
194
|
excelNomFichier: { type: String, default: 'csqc' },
|
|
162
|
-
|
|
163
|
-
//
|
|
195
|
+
|
|
196
|
+
// Choix colonnes
|
|
164
197
|
formulaireId: { type: Number, default: -1 },
|
|
165
|
-
identifiant: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
198
|
+
identifiant: { type: String, default: '' },
|
|
199
|
+
urlbase: { type: String, default: '' },
|
|
200
|
+
permettreChoixColonnes: { type: Boolean, default: false },
|
|
201
|
+
|
|
202
|
+
// Table
|
|
169
203
|
itemKey: { type: String, default: 'id' },
|
|
170
204
|
itemsParPage: { type: Number, default: 10 },
|
|
205
|
+
itemsParPageOptions: { type: Array, default: () => [10, 25, 30, -1] },
|
|
171
206
|
liste: {
|
|
172
207
|
type: Array as PropType<Record<string, any>[]>,
|
|
173
208
|
default: () => [],
|
|
174
209
|
},
|
|
175
210
|
hover: { type: Boolean, default: false },
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
itemsParPageOptions: { type: Array, default: () => [10, 25, 30, -1] },
|
|
179
|
-
//pageCourante: { type: Number, default: 1 },
|
|
180
|
-
//paginationBottom: { type: Boolean, default: true },
|
|
181
|
-
// paginationTop: { type: Boolean, default: false },
|
|
182
|
-
// permettreChoixColonnes: { type: Boolean, default: false },
|
|
183
|
-
// rechercheDepart: { type: String, default: '' },
|
|
211
|
+
|
|
212
|
+
// Recherche
|
|
184
213
|
rechercheTexte: { type: String, default: '' },
|
|
185
214
|
rechercheAfficher: { type: Boolean, default: true },
|
|
186
215
|
rechercheAvancee: { type: Boolean, default: false },
|
|
187
216
|
rechercheAvanceeTexte: { type: String, default: '' },
|
|
188
217
|
rechercheAvanceeLargeur: { type: Number, default: 12 },
|
|
189
218
|
rechercheAvanceeStyle: { type: String, default: '' },
|
|
190
|
-
// selectionChoixColonnesDepart: { type: String, default: '' },
|
|
191
|
-
// showSelect: { type: Boolean, default: false },
|
|
192
219
|
|
|
220
|
+
// Modale suppression
|
|
193
221
|
modaleSupprimerChamp: { type: String, default: '' },
|
|
194
222
|
modaleSupprimerTexte: { type: String, default: '' },
|
|
195
223
|
modaleSupprimerTitre: { type: String, default: '' },
|
|
196
224
|
modaleSupprimerLargeur: { type: String, default: '525px' },
|
|
225
|
+
|
|
226
|
+
// Tri
|
|
197
227
|
triDescDepart: { type: Boolean, default: false },
|
|
198
228
|
triParDepart: {
|
|
199
229
|
type: [Array, String] as PropType<string | string[] | SortItem[] | undefined>,
|
|
200
230
|
default: undefined,
|
|
201
231
|
},
|
|
202
|
-
//urlbase: { type: String, default: '' },
|
|
203
232
|
})
|
|
204
233
|
|
|
205
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
206
|
-
const slots = defineSlots<Slots>()
|
|
207
234
|
const { t } = useI18n({ useScope: 'global' })
|
|
208
|
-
const
|
|
209
|
-
const modaleSupprimer = ref<InstanceType<typeof confirmation> | null>(null)
|
|
210
|
-
const supprimerTexte = computed(() => {
|
|
211
|
-
if (itemSelectionne.value == null) return ''
|
|
212
|
-
|
|
213
|
-
if (props.modaleSupprimerTexte != null && props.modaleSupprimerTexte != '') return props.modaleSupprimerTexte
|
|
235
|
+
const recherche = ref<string>('')
|
|
214
236
|
|
|
215
|
-
|
|
237
|
+
// Modale suppression (on garde l’item sélectionné)
|
|
238
|
+
const itemSelectionne = ref<any>(null)
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// State - Choix de colonnes (vues sauvegardées) + sélection courante
|
|
242
|
+
// ============================================================================
|
|
243
|
+
const modaleChoix = ref<InstanceType<typeof ModaleChoix> | null>(null)
|
|
244
|
+
const openChoixColonnes = ref(false)
|
|
245
|
+
const choix = ref<VueColonnes[]>([]) // la liste des vues disponibles
|
|
246
|
+
const vueActive = ref<VueColonnes | null>(null) // optionnel: la vue "appliquée"
|
|
247
|
+
const valeurSelectionChoixColonnes = ref<string | null>(null) // nomVue sélectionné dans la modale / UI
|
|
248
|
+
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Charger le choix de colonnes sauvegardé s'il y a lieu
|
|
251
|
+
// ============================================================================
|
|
252
|
+
onMounted(async () => {
|
|
253
|
+
if (!props.permettreChoixColonnes) return
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const res: any = await axios
|
|
257
|
+
.getAxios()
|
|
258
|
+
.get(`${props.urlbase}/api/ComposantUI/Colonnes/${props.formulaireId}/Identifiant/${props.identifiant}`)
|
|
259
|
+
|
|
260
|
+
const payload = res?.data ?? res
|
|
261
|
+
|
|
262
|
+
if (!payload) {
|
|
263
|
+
choix.value = []
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
choix.value = typeof payload === 'string' ? JSON.parse(payload) : payload
|
|
268
|
+
} catch (e) {
|
|
269
|
+
choix.value = []
|
|
270
|
+
// console.debug(e)
|
|
271
|
+
}
|
|
216
272
|
})
|
|
217
273
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
//
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// Computed - Normaliser pour la modale Choix colonne
|
|
276
|
+
// ============================================================================
|
|
277
|
+
const choixOrigineNormalise = computed(() =>
|
|
278
|
+
(choix.value ?? []).map(c => ({
|
|
279
|
+
_id: '',
|
|
280
|
+
nomVue: c.nomVue,
|
|
281
|
+
colonnes: c.colonnes ?? [],
|
|
282
|
+
defaut: c.defaut ?? false,
|
|
283
|
+
})),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// Actions - Modale choix colonnes
|
|
288
|
+
// ============================================================================
|
|
289
|
+
function ouvrirChoixColonnes() {
|
|
290
|
+
modaleChoix.value?.ouvrir()
|
|
226
291
|
}
|
|
227
292
|
|
|
228
|
-
|
|
229
|
-
|
|
293
|
+
function appliquerVue(vue: VueColonnes) {
|
|
294
|
+
vueActive.value = vue
|
|
295
|
+
openChoixColonnes.value = false
|
|
230
296
|
}
|
|
231
297
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
298
|
+
function selectionChoix(vue: VueColonnes) {
|
|
299
|
+
// on garde une trace du nom sélectionné (pour retrouver la vue plus tard)
|
|
300
|
+
valeurSelectionChoixColonnes.value = vue.nomVue
|
|
301
|
+
appliquerVue(vue)
|
|
235
302
|
}
|
|
236
303
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
304
|
+
function sauvegardeChoix(vues: VueColonnes[]) {
|
|
305
|
+
if (!props.permettreChoixColonnes) return
|
|
240
306
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
307
|
+
// si la sélection courante n’existe plus, on reset
|
|
308
|
+
if (
|
|
309
|
+
valeurSelectionChoixColonnes.value != null &&
|
|
310
|
+
vues.find(x => x.nomVue === valeurSelectionChoixColonnes.value) == null
|
|
311
|
+
) {
|
|
312
|
+
valeurSelectionChoixColonnes.value = null
|
|
313
|
+
}
|
|
245
314
|
|
|
246
|
-
|
|
247
|
-
|
|
315
|
+
choix.value = vues
|
|
316
|
+
|
|
317
|
+
// optionnel: appliquer automatiquement la vue par défaut
|
|
318
|
+
const defaut = vues.find(v => v.defaut)
|
|
319
|
+
if (defaut) appliquerVue(defaut)
|
|
248
320
|
}
|
|
249
|
-
const filteredItems = computed(() => {
|
|
250
|
-
if (!recherche.value) return props.liste // Si rien écrit, affiche tout
|
|
251
321
|
|
|
252
|
-
|
|
253
|
-
|
|
322
|
+
// ============================================================================
|
|
323
|
+
// Computed - Colonnes à envoyer à la modale (liste ordonnée + libellé)
|
|
324
|
+
// ============================================================================
|
|
325
|
+
const colonnesPourChoix = computed(() =>
|
|
326
|
+
props.colonnes
|
|
327
|
+
// on exclut l'action (souvent toujours visible) + colonnes "cachées"
|
|
328
|
+
.filter(c => c.key && c.key !== 'action' && (c as any).align !== 'd-none')
|
|
329
|
+
.map((c, idx) => ({
|
|
330
|
+
title: (c as any).title ?? (c as any).text ?? String((c as any).key ?? (c as any).value),
|
|
331
|
+
value: String((c as any).key ?? (c as any).value), // ← on force string
|
|
332
|
+
ordre: idx + 1,
|
|
333
|
+
})),
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Computed - Headers réellement affichés par la table (selon le choix actif)
|
|
338
|
+
// ============================================================================
|
|
339
|
+
const colonnesAffichees = computed(() => {
|
|
340
|
+
// 1) colonnes "disponibles" (on enlève celles explicitement cachées)
|
|
341
|
+
const colonnesFiltre = props.colonnes.filter(c => (c as any).align !== 'd-none')
|
|
342
|
+
|
|
343
|
+
// 2) si feature OFF ou pas de vues sauvegardées => on retourne tout
|
|
344
|
+
if (!props.permettreChoixColonnes) return colonnesFiltre
|
|
345
|
+
if (!choix.value?.length) return colonnesFiltre
|
|
346
|
+
|
|
347
|
+
// 3) retrouver la vue sélectionnée (sinon la défaut)
|
|
348
|
+
const choixSelection = choix.value.find(
|
|
349
|
+
x => valeurSelectionChoixColonnes.value === x.nomVue || (!valeurSelectionChoixColonnes.value && x.defaut),
|
|
254
350
|
)
|
|
351
|
+
if (!choixSelection) return colonnesFiltre
|
|
352
|
+
|
|
353
|
+
// 4) reconstruire la liste de headers dans l’ordre sauvegardé
|
|
354
|
+
return choixSelection.colonnes.map(k => colonnesFiltre.find(c => String(c.key) === k)).filter(Boolean) as Colonne[]
|
|
255
355
|
})
|
|
256
356
|
|
|
357
|
+
// ============================================================================
|
|
358
|
+
// Computed - Tri initial (Vuetify sort-by)
|
|
359
|
+
// ============================================================================
|
|
257
360
|
const triDepart = computed<SortItem[] | undefined>(() => {
|
|
258
361
|
if (props.triParDepart == null) return undefined
|
|
259
362
|
|
|
260
363
|
const ordre = props.triDescDepart ? 'desc' : 'asc'
|
|
261
|
-
|
|
262
364
|
if (typeof props.triParDepart === 'string') return [{ key: props.triParDepart, order: ordre }]
|
|
263
365
|
|
|
264
|
-
|
|
265
|
-
let retour: SortItem[] = []
|
|
366
|
+
const retour: SortItem[] = []
|
|
266
367
|
for (let i = 0; i < props.triParDepart.length; i += 1) {
|
|
267
368
|
const tri = props.triParDepart[i]!
|
|
268
|
-
if (typeof tri
|
|
269
|
-
else retour.push(tri)
|
|
369
|
+
if (typeof tri === 'string') retour.push({ key: tri, order: ordre })
|
|
370
|
+
else retour.push(tri)
|
|
270
371
|
}
|
|
271
372
|
return retour
|
|
272
373
|
})
|
|
273
374
|
|
|
274
375
|
const estMultiTriActif = computed(() => {
|
|
275
376
|
if (props.triParDepart == null) return false
|
|
276
|
-
|
|
277
377
|
if (typeof props.triParDepart === 'string') return false
|
|
278
|
-
|
|
279
378
|
return true
|
|
280
379
|
})
|
|
281
380
|
|
|
282
|
-
|
|
381
|
+
// ============================================================================
|
|
382
|
+
// Computed - Export (filtrage local basé sur recherche)
|
|
383
|
+
// ============================================================================
|
|
384
|
+
const filteredItems = computed(() => {
|
|
385
|
+
if (!recherche.value) return props.liste
|
|
386
|
+
const q = recherche.value.toLowerCase()
|
|
387
|
+
return props.liste.filter(item => Object.values(item).some(val => String(val).toLowerCase().includes(q)))
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
// ============================================================================
|
|
391
|
+
// UI Actions - Recherche / Table events
|
|
392
|
+
// ============================================================================
|
|
393
|
+
function chargerRecherche(val: string) {
|
|
394
|
+
recherche.value = val
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function ajouter() {
|
|
398
|
+
emit('ajouter')
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function cliqueLigne(_e: Event, { item }: { item: any }) {
|
|
402
|
+
emit('cliqueLigne', item)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function modifier(item: any) {
|
|
406
|
+
emit('modifier', item)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ============================================================================
|
|
410
|
+
// Suppression - Texte + actions
|
|
411
|
+
// ============================================================================
|
|
412
|
+
const supprimerTexte = computed(() => {
|
|
413
|
+
if (itemSelectionne.value == null) return ''
|
|
414
|
+
|
|
415
|
+
if (props.modaleSupprimerTexte) return props.modaleSupprimerTexte
|
|
416
|
+
|
|
417
|
+
return t('csqc.message.supprimerMessage', {
|
|
418
|
+
nom: itemSelectionne?.value?.[props.modaleSupprimerChamp] ?? '',
|
|
419
|
+
})
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
const supprimerTitreTexte = computed(() => props.modaleSupprimerTitre || t('csqc.message.supprimerTitre'))
|
|
423
|
+
|
|
424
|
+
function ouvrirModaleSupprimer(item: any) {
|
|
425
|
+
itemSelectionne.value = item
|
|
426
|
+
emit('supprimer', itemSelectionne.value) // tu sembles gérer la modale ailleurs
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function supprimer() {
|
|
430
|
+
emit('supprimer', itemSelectionne.value)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ============================================================================
|
|
434
|
+
// UI - Panneau recherche avancée
|
|
435
|
+
// ============================================================================
|
|
436
|
+
function onPanelChange(val: boolean) {
|
|
283
437
|
emit('panneau:etat', val)
|
|
284
438
|
}
|
|
285
|
-
const emit = defineEmits(['ajouter', 'cliqueLigne', 'supprimer', 'modifier', 'donneesExportees', 'panneau:etat'])
|
|
286
439
|
</script>
|
|
287
440
|
|
|
288
441
|
<style scoped lang="css">
|