codevdesign 1.0.22 → 1.0.23

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.
@@ -1,54 +1,54 @@
1
- <template>
2
- <div>
3
- <v-list-item-title>{{ nomUnite }}</v-list-item-title>
4
- <v-list-item-subtitle v-if="getPreference != ''">
5
- {{ getPreference }}
6
- </v-list-item-subtitle>
7
- <v-list-item-subtitle
8
- v-if="getPreference == ''"
9
- class="messageErreurChaiseItem"
10
- >
11
- {{ $t('csqc.message.chaiseSelection') }}
12
- </v-list-item-subtitle>
13
- </div>
14
- </template>
15
- <script lang="ts" setup>
16
- import { computed } from 'vue'
17
-
18
- // Définition des props
19
- const props = defineProps<{
20
- uniteId: number
21
- preferences: { chaiseId: number }[]
22
- dictChaisesReleve: Record<number, { id: number; nom: string }[]>
23
- unites: { id: number; nom: string }[]
24
- }>()
25
-
26
- const nomUnite = computed(() => {
27
- if (!props.uniteId || !props.unites?.length) {
28
- return ''
29
- }
30
-
31
- const unite = props.unites.find(u => u.id == props.uniteId)
32
- return unite?.nom || ''
33
- })
34
-
35
- const getPreference = computed(() => {
36
- const chaises = props.dictChaisesReleve?.[props.uniteId] ?? []
37
- const prefs = props.preferences ?? []
38
-
39
- for (const chaise of chaises) {
40
- if (prefs.some(p => p.chaiseId === chaise.id)) {
41
- return chaise.nom
42
- }
43
- }
44
-
45
- return ''
46
- })
47
- </script>
48
-
49
- <style scoped>
50
- .messageErreurChaiseItem {
51
- color: red !important;
52
- font-style: italic;
53
- }
54
- </style>
1
+ <template>
2
+ <div>
3
+ <v-list-item-title>{{ nomUnite }}</v-list-item-title>
4
+ <v-list-item-subtitle v-if="getPreference != ''">
5
+ {{ getPreference }}
6
+ </v-list-item-subtitle>
7
+ <v-list-item-subtitle
8
+ v-if="getPreference == ''"
9
+ class="messageErreurChaiseItem"
10
+ >
11
+ {{ $t('csqc.message.chaiseSelection') }}
12
+ </v-list-item-subtitle>
13
+ </div>
14
+ </template>
15
+ <script lang="ts" setup>
16
+ import { computed } from 'vue'
17
+
18
+ // Définition des props
19
+ const props = defineProps<{
20
+ uniteId: number
21
+ preferences: { chaiseId: number }[]
22
+ dictChaisesReleve: Record<number, { id: number; nom: string }[]>
23
+ unites: { id: number; nom: string }[]
24
+ }>()
25
+
26
+ const nomUnite = computed(() => {
27
+ if (!props.uniteId || !props.unites?.length) {
28
+ return ''
29
+ }
30
+
31
+ const unite = props.unites.find(u => u.id == props.uniteId)
32
+ return unite?.nom || ''
33
+ })
34
+
35
+ const getPreference = computed(() => {
36
+ const chaises = props.dictChaisesReleve?.[props.uniteId] ?? []
37
+ const prefs = props.preferences ?? []
38
+
39
+ for (const chaise of chaises) {
40
+ if (prefs.some(p => p.chaiseId === chaise.id)) {
41
+ return chaise.nom
42
+ }
43
+ }
44
+
45
+ return ''
46
+ })
47
+ </script>
48
+
49
+ <style scoped>
50
+ .messageErreurChaiseItem {
51
+ color: red !important;
52
+ font-style: italic;
53
+ }
54
+ </style>
@@ -1,280 +1,280 @@
1
- <template>
2
- <div>
3
- <v-form
4
- ref="form"
5
- v-model="formValide"
6
- @submit.prevent
7
- >
8
- <v-combobox
9
- v-model="codeBudgetaire"
10
- :items="codeBudgetairesProp"
11
- v-bind="$attrs"
12
- persistent-hint
13
- variant="outlined"
14
- hide-details="auto"
15
- :error="!estValideComplet"
16
- :rules="reglesVuetify"
17
- :hint="afficherHint ? placeholder : ''"
18
- @blur="sauvegarder"
19
- @keydown.enter="sauvegarder"
20
- @keydown="caractereAutorises"
21
- @update:modelValue="gererChangement"
22
- @paste="gererPaste"
23
- />
24
- </v-form>
25
- </div>
26
- </template>
27
-
28
- <script setup lang="ts">
29
- import { ref, computed, onMounted, nextTick, watch } from 'vue'
30
- import type { VForm } from 'vuetify/components'
31
-
32
- const emit = defineEmits<{
33
- 'update:modelValue': [string | null]
34
- 'update:codeBudgetairesProp': [string[]]
35
- 'update:valide': [boolean] // 👈 nouveau
36
- }>()
37
-
38
- const props = withDefaults(
39
- defineProps<{
40
- codeBudgetairesProp: string[]
41
- modelValue: string | null
42
- afficherHint?: boolean
43
- regleMessageErreur: string
44
- format?: string
45
- activerExtension?: boolean
46
- reglesSupp?: ((v: string) => true | string)[]
47
- }>(),
48
- {
49
- afficherHint: false,
50
- format: '999-9-99999-999',
51
- activerExtension: false,
52
- reglesSupp: () => [],
53
- },
54
- )
55
-
56
- const formValide = ref(false)
57
- const form = ref<VForm | null>(null)
58
- const codeBudgetaire = ref(props.modelValue ?? '')
59
- const derniereValeurSauvegardee = ref<string | null>(null)
60
- const format = props.format
61
- const activerExtension = props.activerExtension
62
-
63
- onMounted(() => {
64
- derniereValeurSauvegardee.value = codeBudgetaire.value
65
- })
66
-
67
- const placeholder = computed(() => {
68
- const base = format.replace(/9/g, '0')
69
- const extension = activerExtension ? '-XXX/XXX' : ''
70
- return base + extension
71
- })
72
-
73
- const estValide = computed(() => {
74
- const val = codeBudgetaire.value?.toUpperCase().trim() || ''
75
- const base = val.slice(0, 15)
76
- const extension = val.slice(15)
77
-
78
- if (!/^\d{3}-\d{1}-\d{5}-\d{3}$/.test(base)) return false
79
-
80
- if (!activerExtension) return val.length === 15
81
-
82
- if (val.length === 15) return true
83
- if (val.length !== 22) return false
84
- if (extension.length !== 7) return false
85
-
86
- if (extension[3] !== '/') return false
87
- if (!/^[A-Z0-9/]$/i.test(extension[0]!)) return false
88
-
89
- const alphanumAt = [1, 2, 4, 5, 6]
90
- return alphanumAt.every(i => /^[A-Z0-9]$/i.test(extension[i]!))
91
- })
92
-
93
- // Règles Vuetify combinées (interne + supplémentaires)
94
- const reglesVuetify = computed(() => [
95
- // règle interne
96
- () => (estValide.value ? true : props.regleMessageErreur),
97
-
98
- // règles supplémentaires venant du parent
99
- ...props.reglesSupp.map(rule => {
100
- return () => rule(codeBudgetaire.value) // on passe la valeur formatée
101
- }),
102
- ])
103
-
104
- // Validité globale du composant (interne + toutes les règles supp)
105
- const estValideComplet = computed(() => {
106
- if (!estValide.value) return false
107
- return props.reglesSupp.every(rule => rule(codeBudgetaire.value) === true)
108
- })
109
-
110
- const caractereAutorises = (e: KeyboardEvent) => {
111
- if (e.ctrlKey || e.metaKey) return
112
-
113
- const touchesSpecifiques = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Home', 'End']
114
- if (touchesSpecifiques.includes(e.key)) return
115
-
116
- const input = e.target as HTMLInputElement
117
- let position = input.selectionStart ?? 0
118
-
119
- // Gestion de la partie de base (15 premiers caractères)
120
- if (position < 15) {
121
- if (!/^\d$/.test(e.key)) {
122
- e.preventDefault()
123
- return
124
- }
125
-
126
- // Insérer chiffre et auto-ajout des tirets
127
- e.preventDefault()
128
-
129
- const value = codeBudgetaire.value.replace(/-/g, '')
130
- const clean = value.slice(0, 12) + e.key
131
-
132
- let formatted = ''
133
- if (clean.length > 0) formatted += clean.slice(0, 3)
134
- if (clean.length > 3) formatted += '-' + clean.slice(3, 4)
135
- if (clean.length > 4) formatted += '-' + clean.slice(4, 9)
136
- if (clean.length > 9) formatted += '-' + clean.slice(9, 12)
137
-
138
- codeBudgetaire.value = formatted.slice(0, 15)
139
-
140
- nextTick(() => {
141
- const newPos = codeBudgetaire.value.length
142
- input.selectionStart = input.selectionEnd = newPos
143
- })
144
-
145
- return
146
- }
147
-
148
- // --- Gestion de l'extension ---
149
- if (!activerExtension || position >= 22 || codeBudgetaire.value.length >= 22) {
150
- e.preventDefault()
151
- return
152
- }
153
-
154
- const extensionPos = position - 15
155
-
156
- // Règle 1 : extension[0] = alphanum ou /
157
- if (extensionPos === 0) {
158
- if (!/^[A-Z0-9/]$/i.test(e.key)) {
159
- e.preventDefault()
160
- return
161
- }
162
- return
163
- }
164
-
165
- // Règle 2 : extension[1,2,4,5,6] = alphanum
166
- if ([1, 2, 4, 5, 6].includes(extensionPos)) {
167
- if (!/^[A-Z0-9]$/i.test(e.key)) {
168
- e.preventDefault()
169
- return
170
- }
171
- return
172
- }
173
-
174
- // Règle 3 : slash automatique à position 3 (index 18)
175
- if (extensionPos === 3) {
176
- e.preventDefault()
177
- const before = codeBudgetaire.value.slice(0, position)
178
- const after = codeBudgetaire.value.slice(position)
179
- codeBudgetaire.value = before + '/' + after
180
- nextTick(() => {
181
- input.selectionStart = input.selectionEnd = position + 1
182
- })
183
- return
184
- }
185
-
186
- // Tout autre cas = bloqué
187
- e.preventDefault()
188
- }
189
-
190
- const formaterCodeBudgetaire = (valeur: string): string => {
191
- if (!valeur) return ''
192
-
193
- const upper = valeur.toUpperCase().replace(/[^A-Z0-9/]/g, '')
194
- const chiffres = upper.replace(/[^0-9]/g, '').slice(0, 12)
195
-
196
- let base = ''
197
- if (chiffres.length > 0) base += chiffres.slice(0, 3)
198
- if (chiffres.length > 3) base += '-' + chiffres.slice(3, 4)
199
- if (chiffres.length > 4) base += '-' + chiffres.slice(4, 9)
200
- if (chiffres.length > 9) base += '-' + chiffres.slice(9, 12)
201
-
202
- if (!activerExtension || base.length < 15) return base
203
-
204
- const reste = upper.slice(chiffres.length).replace(/[^A-Z0-9/]/gi, '')
205
-
206
- // Extraire les 7 premiers caractères restants pour l’extension
207
- let ext = reste.slice(0, 7).split('')
208
-
209
- // Ne garder que le premier slash s’il est à l’index 0 ou 3
210
- ext = ext.filter((c, i) => {
211
- if (c !== '/') return true
212
- return i === 0 || i === 3
213
- })
214
-
215
- // Enlever les slash non autorisés
216
- ext = ext.map((c, i) => {
217
- if (c === '/' && i !== 0 && i !== 3) return ''
218
- if (c !== '/' && !/^[A-Z0-9]$/i.test(c)) return ''
219
- return c
220
- })
221
-
222
- // Forcer le slash à la 4e position
223
- if (ext.length > 3) {
224
- ext[3] = '/'
225
- }
226
-
227
- // Réduire à 7 caractères
228
- ext = ext.slice(0, 7)
229
-
230
- return (base + ext.join('')).slice(0, 22)
231
- }
232
-
233
- const gererPaste = (e: ClipboardEvent) => {
234
- e.preventDefault()
235
- const clipboardData = e.clipboardData
236
- if (!clipboardData) return
237
- let pasted = clipboardData.getData('text') || ''
238
- codeBudgetaire.value = formaterCodeBudgetaire(pasted)
239
-
240
- setTimeout(() => {
241
- const input = e.target as HTMLInputElement
242
- input.selectionStart = input.selectionEnd = codeBudgetaire.value.length
243
- }, 0)
244
- }
245
-
246
- const sauvegarder = () => {
247
- codeBudgetaire.value = formaterCodeBudgetaire(codeBudgetaire.value)
248
-
249
- // on utilise la validité globale
250
- if (!estValideComplet.value) return
251
- if (codeBudgetaire.value === derniereValeurSauvegardee.value) return
252
-
253
- const existe = props.codeBudgetairesProp.some(
254
- item => item.trim().toUpperCase() === codeBudgetaire.value.trim().toUpperCase(),
255
- )
256
-
257
- if (!existe) {
258
- const nouvelleListe = [...props.codeBudgetairesProp, codeBudgetaire.value]
259
- emit('update:codeBudgetairesProp', nouvelleListe)
260
- }
261
-
262
- derniereValeurSauvegardee.value = codeBudgetaire.value
263
- emit('update:modelValue', codeBudgetaire.value)
264
- }
265
-
266
- const gererChangement = (val: string) => {
267
- codeBudgetaire.value = formaterCodeBudgetaire(val)
268
-
269
- const valeurFormatee = codeBudgetaire.value
270
- const estDansListe = props.codeBudgetairesProp.includes(valeurFormatee)
271
-
272
- if (estDansListe && valeurFormatee !== derniereValeurSauvegardee.value && estValideComplet.value) {
273
- sauvegarder()
274
- }
275
- }
276
-
277
- watch(estValideComplet, value => {
278
- emit('update:valide', value)
279
- })
280
- </script>
1
+ <template>
2
+ <div>
3
+ <v-form
4
+ ref="form"
5
+ v-model="formValide"
6
+ @submit.prevent
7
+ >
8
+ <v-combobox
9
+ v-model="codeBudgetaire"
10
+ :items="codeBudgetairesProp"
11
+ v-bind="$attrs"
12
+ persistent-hint
13
+ variant="outlined"
14
+ hide-details="auto"
15
+ :error="!estValideComplet"
16
+ :rules="reglesVuetify"
17
+ :hint="afficherHint ? placeholder : ''"
18
+ @blur="sauvegarder"
19
+ @keydown.enter="sauvegarder"
20
+ @keydown="caractereAutorises"
21
+ @update:modelValue="gererChangement"
22
+ @paste="gererPaste"
23
+ />
24
+ </v-form>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import { ref, computed, onMounted, nextTick, watch } from 'vue'
30
+ import type { VForm } from 'vuetify/components'
31
+
32
+ const emit = defineEmits<{
33
+ 'update:modelValue': [string | null]
34
+ 'update:codeBudgetairesProp': [string[]]
35
+ 'update:valide': [boolean] // 👈 nouveau
36
+ }>()
37
+
38
+ const props = withDefaults(
39
+ defineProps<{
40
+ codeBudgetairesProp: string[]
41
+ modelValue: string | null
42
+ afficherHint?: boolean
43
+ regleMessageErreur: string
44
+ format?: string
45
+ activerExtension?: boolean
46
+ reglesSupp?: ((v: string) => true | string)[]
47
+ }>(),
48
+ {
49
+ afficherHint: false,
50
+ format: '999-9-99999-999',
51
+ activerExtension: false,
52
+ reglesSupp: () => [],
53
+ },
54
+ )
55
+
56
+ const formValide = ref(false)
57
+ const form = ref<VForm | null>(null)
58
+ const codeBudgetaire = ref(props.modelValue ?? '')
59
+ const derniereValeurSauvegardee = ref<string | null>(null)
60
+ const format = props.format
61
+ const activerExtension = props.activerExtension
62
+
63
+ onMounted(() => {
64
+ derniereValeurSauvegardee.value = codeBudgetaire.value
65
+ })
66
+
67
+ const placeholder = computed(() => {
68
+ const base = format.replace(/9/g, '0')
69
+ const extension = activerExtension ? '-XXX/XXX' : ''
70
+ return base + extension
71
+ })
72
+
73
+ const estValide = computed(() => {
74
+ const val = codeBudgetaire.value?.toUpperCase().trim() || ''
75
+ const base = val.slice(0, 15)
76
+ const extension = val.slice(15)
77
+
78
+ if (!/^\d{3}-\d{1}-\d{5}-\d{3}$/.test(base)) return false
79
+
80
+ if (!activerExtension) return val.length === 15
81
+
82
+ if (val.length === 15) return true
83
+ if (val.length !== 22) return false
84
+ if (extension.length !== 7) return false
85
+
86
+ if (extension[3] !== '/') return false
87
+ if (!/^[A-Z0-9/]$/i.test(extension[0]!)) return false
88
+
89
+ const alphanumAt = [1, 2, 4, 5, 6]
90
+ return alphanumAt.every(i => /^[A-Z0-9]$/i.test(extension[i]!))
91
+ })
92
+
93
+ // Règles Vuetify combinées (interne + supplémentaires)
94
+ const reglesVuetify = computed(() => [
95
+ // règle interne
96
+ () => (estValide.value ? true : props.regleMessageErreur),
97
+
98
+ // règles supplémentaires venant du parent
99
+ ...props.reglesSupp.map(rule => {
100
+ return () => rule(codeBudgetaire.value) // on passe la valeur formatée
101
+ }),
102
+ ])
103
+
104
+ // Validité globale du composant (interne + toutes les règles supp)
105
+ const estValideComplet = computed(() => {
106
+ if (!estValide.value) return false
107
+ return props.reglesSupp.every(rule => rule(codeBudgetaire.value) === true)
108
+ })
109
+
110
+ const caractereAutorises = (e: KeyboardEvent) => {
111
+ if (e.ctrlKey || e.metaKey) return
112
+
113
+ const touchesSpecifiques = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Home', 'End']
114
+ if (touchesSpecifiques.includes(e.key)) return
115
+
116
+ const input = e.target as HTMLInputElement
117
+ let position = input.selectionStart ?? 0
118
+
119
+ // Gestion de la partie de base (15 premiers caractères)
120
+ if (position < 15) {
121
+ if (!/^\d$/.test(e.key)) {
122
+ e.preventDefault()
123
+ return
124
+ }
125
+
126
+ // Insérer chiffre et auto-ajout des tirets
127
+ e.preventDefault()
128
+
129
+ const value = codeBudgetaire.value.replace(/-/g, '')
130
+ const clean = value.slice(0, 12) + e.key
131
+
132
+ let formatted = ''
133
+ if (clean.length > 0) formatted += clean.slice(0, 3)
134
+ if (clean.length > 3) formatted += '-' + clean.slice(3, 4)
135
+ if (clean.length > 4) formatted += '-' + clean.slice(4, 9)
136
+ if (clean.length > 9) formatted += '-' + clean.slice(9, 12)
137
+
138
+ codeBudgetaire.value = formatted.slice(0, 15)
139
+
140
+ nextTick(() => {
141
+ const newPos = codeBudgetaire.value.length
142
+ input.selectionStart = input.selectionEnd = newPos
143
+ })
144
+
145
+ return
146
+ }
147
+
148
+ // --- Gestion de l'extension ---
149
+ if (!activerExtension || position >= 22 || codeBudgetaire.value.length >= 22) {
150
+ e.preventDefault()
151
+ return
152
+ }
153
+
154
+ const extensionPos = position - 15
155
+
156
+ // Règle 1 : extension[0] = alphanum ou /
157
+ if (extensionPos === 0) {
158
+ if (!/^[A-Z0-9/]$/i.test(e.key)) {
159
+ e.preventDefault()
160
+ return
161
+ }
162
+ return
163
+ }
164
+
165
+ // Règle 2 : extension[1,2,4,5,6] = alphanum
166
+ if ([1, 2, 4, 5, 6].includes(extensionPos)) {
167
+ if (!/^[A-Z0-9]$/i.test(e.key)) {
168
+ e.preventDefault()
169
+ return
170
+ }
171
+ return
172
+ }
173
+
174
+ // Règle 3 : slash automatique à position 3 (index 18)
175
+ if (extensionPos === 3) {
176
+ e.preventDefault()
177
+ const before = codeBudgetaire.value.slice(0, position)
178
+ const after = codeBudgetaire.value.slice(position)
179
+ codeBudgetaire.value = before + '/' + after
180
+ nextTick(() => {
181
+ input.selectionStart = input.selectionEnd = position + 1
182
+ })
183
+ return
184
+ }
185
+
186
+ // Tout autre cas = bloqué
187
+ e.preventDefault()
188
+ }
189
+
190
+ const formaterCodeBudgetaire = (valeur: string): string => {
191
+ if (!valeur) return ''
192
+
193
+ const upper = valeur.toUpperCase().replace(/[^A-Z0-9/]/g, '')
194
+ const chiffres = upper.replace(/[^0-9]/g, '').slice(0, 12)
195
+
196
+ let base = ''
197
+ if (chiffres.length > 0) base += chiffres.slice(0, 3)
198
+ if (chiffres.length > 3) base += '-' + chiffres.slice(3, 4)
199
+ if (chiffres.length > 4) base += '-' + chiffres.slice(4, 9)
200
+ if (chiffres.length > 9) base += '-' + chiffres.slice(9, 12)
201
+
202
+ if (!activerExtension || base.length < 15) return base
203
+
204
+ const reste = upper.slice(chiffres.length).replace(/[^A-Z0-9/]/gi, '')
205
+
206
+ // Extraire les 7 premiers caractères restants pour l’extension
207
+ let ext = reste.slice(0, 7).split('')
208
+
209
+ // Ne garder que le premier slash s’il est à l’index 0 ou 3
210
+ ext = ext.filter((c, i) => {
211
+ if (c !== '/') return true
212
+ return i === 0 || i === 3
213
+ })
214
+
215
+ // Enlever les slash non autorisés
216
+ ext = ext.map((c, i) => {
217
+ if (c === '/' && i !== 0 && i !== 3) return ''
218
+ if (c !== '/' && !/^[A-Z0-9]$/i.test(c)) return ''
219
+ return c
220
+ })
221
+
222
+ // Forcer le slash à la 4e position
223
+ if (ext.length > 3) {
224
+ ext[3] = '/'
225
+ }
226
+
227
+ // Réduire à 7 caractères
228
+ ext = ext.slice(0, 7)
229
+
230
+ return (base + ext.join('')).slice(0, 22)
231
+ }
232
+
233
+ const gererPaste = (e: ClipboardEvent) => {
234
+ e.preventDefault()
235
+ const clipboardData = e.clipboardData
236
+ if (!clipboardData) return
237
+ let pasted = clipboardData.getData('text') || ''
238
+ codeBudgetaire.value = formaterCodeBudgetaire(pasted)
239
+
240
+ setTimeout(() => {
241
+ const input = e.target as HTMLInputElement
242
+ input.selectionStart = input.selectionEnd = codeBudgetaire.value.length
243
+ }, 0)
244
+ }
245
+
246
+ const sauvegarder = () => {
247
+ codeBudgetaire.value = formaterCodeBudgetaire(codeBudgetaire.value)
248
+
249
+ // on utilise la validité globale
250
+ if (!estValideComplet.value) return
251
+ if (codeBudgetaire.value === derniereValeurSauvegardee.value) return
252
+
253
+ const existe = props.codeBudgetairesProp.some(
254
+ item => item.trim().toUpperCase() === codeBudgetaire.value.trim().toUpperCase(),
255
+ )
256
+
257
+ if (!existe) {
258
+ const nouvelleListe = [...props.codeBudgetairesProp, codeBudgetaire.value]
259
+ emit('update:codeBudgetairesProp', nouvelleListe)
260
+ }
261
+
262
+ derniereValeurSauvegardee.value = codeBudgetaire.value
263
+ emit('update:modelValue', codeBudgetaire.value)
264
+ }
265
+
266
+ const gererChangement = (val: string) => {
267
+ codeBudgetaire.value = formaterCodeBudgetaire(val)
268
+
269
+ const valeurFormatee = codeBudgetaire.value
270
+ const estDansListe = props.codeBudgetairesProp.includes(valeurFormatee)
271
+
272
+ if (estDansListe && valeurFormatee !== derniereValeurSauvegardee.value && estValideComplet.value) {
273
+ sauvegarder()
274
+ }
275
+ }
276
+
277
+ watch(estValideComplet, value => {
278
+ emit('update:valide', value)
279
+ })
280
+ </script>