codevdesign 1.0.78 → 2.0.0
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/assets/csqc.css +28 -30
- package/composants/csqcAide.vue +1 -1
- package/composants/csqcAlerteErreur.vue +1 -1
- package/composants/csqcChaise/chaiseConteneur.vue +4 -4
- package/composants/csqcChaise/chaiseItem.vue +54 -54
- package/composants/csqcCodeBudgetaireGenerique.vue +260 -256
- package/composants/csqcConfirmation.vue +76 -75
- package/composants/csqcDate.vue +88 -86
- package/composants/csqcDialogue.vue +120 -118
- package/composants/csqcEditeurTexteRiche.vue +378 -378
- package/composants/csqcEntete.vue +17 -17
- package/composants/csqcImportCSV.vue +2 -2
- package/composants/csqcModaleSaisie.vue +97 -97
- package/composants/csqcRecherche.vue +9 -9
- package/composants/csqcRechercheUtilisateur.vue +198 -198
- package/composants/csqcSnackbar.vue +207 -207
- package/composants/csqcSwitch.vue +6 -6
- package/composants/csqcTable/csqcTable.vue +19 -14
- package/composants/csqcTable/csqcTableModaleChoixColonnes.vue +5 -5
- package/composants/csqcTable/sortableDataTable.ts +1 -1
- package/composants/csqcTexteBilingue.vue +1 -1
- package/composants/csqcTiroir.vue +197 -197
- package/composants/gabarit/csqcMenu.vue +4 -4
- package/composants/gabarit/pivEntete.vue +6 -5
- package/composants/gabarit/pivPiedPage.vue +44 -29
- package/composants/validateurs.ts +8 -2
- package/editeur.ts +1 -1
- package/importCSV.ts +1 -1
- package/index.ts +80 -80
- package/locales/en.json +1 -1
- package/locales/fr.json +3 -3
- package/modeles/assurancesAssuranceGeneraleGrics.ts +3 -3
- package/modeles/assurancesAssurancePersonnelleGrics.ts +6 -6
- package/modeles/assurancesContratGrics.ts +6 -6
- package/modeles/assurancesDetailsPrimeReguliereGrics.ts +4 -4
- package/modeles/assurancesDonneesAssureurGrics.ts +5 -5
- package/modeles/assurancesEmployeGrics.ts +4 -4
- package/modeles/assurancesGrics.ts +6 -6
- package/modeles/assurancesRegimeAssuranceGrics.ts +2 -2
- package/modeles/assurancesRegimeBaseEmployeurGrics.ts +2 -2
- package/modeles/assurancesRegimeBaseGrics.ts +2 -2
- package/modeles/composants/csqcMenuModele.ts +18 -18
- package/modeles/composants/datatableColonne.ts +19 -19
- package/modeles/employeAdresseGrics.ts +6 -6
- package/modeles/employeAdressesPersonnellesGrics.ts +4 -4
- package/modeles/employeAffectationCorpsEmploiGrics.ts +2 -2
- package/modeles/employeBanquesCongeBanqueGrics.ts +2 -2
- package/modeles/employeBanquesCongeGrics.ts +6 -6
- package/modeles/employeBanquesCongeRegimeAbsenceGrics.ts +2 -2
- package/modeles/employeCourrielsPersonnels.ts +2 -2
- package/modeles/employeCourrielsProfessionnels.ts +2 -2
- package/modeles/employeEmploisCategorieGrics.ts +2 -2
- package/modeles/employeEmploisClasseGrics.ts +2 -2
- package/modeles/employeEmploisCorpsEmploiGrics.ts +2 -2
- package/modeles/employeEmploisEtatEmploiGrics.ts +2 -2
- package/modeles/employeEmploisGrics.ts +29 -29
- package/modeles/employeEmploisGroupePaieGrics.ts +2 -2
- package/modeles/employeEmploisLieuTravailPrincipalGrics.ts +3 -3
- package/modeles/employeEmploisLieuxTravailSecondairesGrics.ts +3 -3
- package/modeles/employeEmploisRegimeAbsenceGrics.ts +2 -2
- package/modeles/employeEmploisSecteurGrics.ts +2 -2
- package/modeles/employeEmploisStatutEngagementGrics.ts +2 -2
- package/modeles/employeExperienceEmploiGrics.ts +2 -2
- package/modeles/employeExperienceEmployeGrics.ts +5 -5
- package/modeles/employeExperienceExperiencesGrics.ts +4 -4
- package/modeles/employeExperienceExperiencesTotalesGrics.ts +7 -7
- package/modeles/employeExperienceGrics.ts +9 -9
- package/modeles/employeGrics.ts +23 -23
- package/modeles/employeTelephoneGrics.ts +4 -4
- package/modeles/employeTelephonesPersonnelsGrics.ts +3 -3
- package/modeles/employeTelephonesProfessionnelsGrics.ts +3 -3
- package/modeles/groupeCE.ts +6 -6
- package/modeles/groupeCEIntervalle.ts +6 -6
- package/modeles/historiquesAbsenceBanqueGrics.ts +2 -2
- package/modeles/historiquesAbsenceGrics.ts +13 -13
- package/modeles/historiquesAbsenceLieuTravailGrics.ts +2 -2
- package/modeles/historiquesAbsenceSousBanqueGrics.ts +2 -2
- package/modeles/motifsAbsenceBanque.ts +2 -2
- package/modeles/motifsAbsenceGrics.ts +9 -9
- package/modeles/motifsAbsenceRegimeAbsence.ts +2 -2
- package/modeles/motifsAbsenceSousMotifs.ts +2 -2
- package/modeles/motifsAbsenceTraitementBanques.ts +3 -3
- package/modeles/syndicat.ts +18 -18
- package/modeles/syndicatGroupeCe.ts +3 -3
- package/modeles/syndicatResponsable.ts +8 -8
- package/modeles/syndicatUnite.ts +3 -3
- package/modeles/unite.ts +15 -15
- package/modeles/uniteTypeEnseignement.ts +4 -4
- package/modeles/utilisateur.ts +8 -8
- package/outils/appAxios.ts +16 -16
- package/outils/csqcOutils.ts +6 -5
- package/outils/csqcRafraichisseurTokenParent.ts +20 -4
- package/outils/rafraichisseurToken.ts +1 -1
- package/package.json +13 -13
|
@@ -19,16 +19,15 @@
|
|
|
19
19
|
:rules="reglesVuetify"
|
|
20
20
|
:hint="afficherHint ? placeholder : ''"
|
|
21
21
|
@blur="sauvegarder"
|
|
22
|
-
@keydown.enter="sauvegarder"
|
|
23
22
|
@keydown="caractereAutorises"
|
|
24
|
-
@update:
|
|
23
|
+
@update:model-value="gererChangement"
|
|
25
24
|
@paste="gererPaste"
|
|
26
25
|
>
|
|
27
26
|
<template #item="{ props, item }">
|
|
28
27
|
<v-list-item
|
|
29
28
|
v-bind="props"
|
|
30
|
-
:title="item.
|
|
31
|
-
:subtitle="item.
|
|
29
|
+
:title="item.nom || item.code"
|
|
30
|
+
:subtitle="item.nom ? item.code : undefined"
|
|
32
31
|
/>
|
|
33
32
|
</template>
|
|
34
33
|
</v-combobox>
|
|
@@ -37,294 +36,299 @@
|
|
|
37
36
|
</template>
|
|
38
37
|
|
|
39
38
|
<script setup lang="ts">
|
|
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
|
-
const formValide = ref(false)
|
|
69
|
-
const form = ref<VForm | null>(null)
|
|
70
|
-
const codeBudgetaire = ref(props.modelValue ?? '')
|
|
71
|
-
const derniereValeurSauvegardee = ref<string | null>(null)
|
|
72
|
-
const format = props.format
|
|
73
|
-
const activerExtension = props.activerExtension
|
|
74
|
-
|
|
75
|
-
onMounted(() => {
|
|
76
|
-
derniereValeurSauvegardee.value = codeBudgetaire.value
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
const itemsCombobox = computed(() => {
|
|
80
|
-
return props.codeBudgetairesProp.map(item => {
|
|
81
|
-
if (typeof item === 'string') {
|
|
82
|
-
return {
|
|
83
|
-
code: item,
|
|
84
|
-
nom: '', // pas de nom
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const [code, nomBrut] = item
|
|
89
|
-
const nom = (nomBrut ?? '').toString().trim()
|
|
90
|
-
|
|
91
|
-
return { code, nom }
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const placeholder = computed(() => {
|
|
96
|
-
const base = format.replace(/9/g, '0')
|
|
97
|
-
const extension = activerExtension ? '-XXX/XXX' : ''
|
|
98
|
-
return base + extension
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const estValide = computed(() => {
|
|
102
|
-
const val = codeBudgetaire.value?.toUpperCase().trim() || ''
|
|
103
|
-
const base = val.slice(0, 15)
|
|
104
|
-
const extension = val.slice(15)
|
|
105
|
-
|
|
106
|
-
if (!/^\d{3}-\d{1}-\d{5}-\d{3}$/.test(base)) return false
|
|
107
|
-
|
|
108
|
-
if (!activerExtension) return val.length === 15
|
|
109
|
-
|
|
110
|
-
if (val.length === 15) return true
|
|
111
|
-
if (val.length !== 22) return false
|
|
112
|
-
if (extension.length !== 7) return false
|
|
113
|
-
|
|
114
|
-
if (extension[3] !== '/') return false
|
|
115
|
-
if (!/^[A-Z0-9/]$/i.test(extension[0]!)) return false
|
|
39
|
+
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
|
40
|
+
import type { VForm } from 'vuetify/components'
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits<{
|
|
43
|
+
'update:modelValue': [string | null]
|
|
44
|
+
'update:codeBudgetairesProp': [CodeBudgetaireItem[]]
|
|
45
|
+
'update:valide': [boolean]
|
|
46
|
+
}>()
|
|
47
|
+
type CodeBudgetaireItem = string | [string, string]
|
|
48
|
+
|
|
49
|
+
const props = withDefaults(
|
|
50
|
+
defineProps<{
|
|
51
|
+
codeBudgetairesProp: CodeBudgetaireItem[]
|
|
52
|
+
modelValue: string | null
|
|
53
|
+
afficherHint?: boolean
|
|
54
|
+
regleMessageErreur: string
|
|
55
|
+
format?: string
|
|
56
|
+
activerExtension?: boolean
|
|
57
|
+
reglesSupp?: ((v: string) => true | string)[]
|
|
58
|
+
}>(),
|
|
59
|
+
{
|
|
60
|
+
afficherHint: false,
|
|
61
|
+
format: '999-9-99999-999',
|
|
62
|
+
activerExtension: false,
|
|
63
|
+
reglesSupp: () => [],
|
|
64
|
+
},
|
|
65
|
+
)
|
|
116
66
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
67
|
+
const formValide = ref(false)
|
|
68
|
+
const form = ref<VForm | null>(null)
|
|
69
|
+
const codeBudgetaire = ref(props.modelValue ?? '')
|
|
70
|
+
const derniereValeurSauvegardee = ref<string | null>(null)
|
|
71
|
+
const format = props.format
|
|
72
|
+
const activerExtension = props.activerExtension
|
|
73
|
+
|
|
74
|
+
onMounted(() => {
|
|
75
|
+
derniereValeurSauvegardee.value = codeBudgetaire.value
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const itemsCombobox = computed(() => {
|
|
79
|
+
return props.codeBudgetairesProp.map(item => {
|
|
80
|
+
if (typeof item === 'string') {
|
|
81
|
+
return {
|
|
82
|
+
code: item,
|
|
83
|
+
nom: '', // pas de nom
|
|
84
|
+
}
|
|
85
|
+
}
|
|
120
86
|
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
// règle interne
|
|
124
|
-
() => (estValide.value ? true : props.regleMessageErreur),
|
|
125
|
-
|
|
126
|
-
// règles supplémentaires venant du parent
|
|
127
|
-
...props.reglesSupp.map(rule => {
|
|
128
|
-
return () => rule(codeBudgetaire.value) // on passe la valeur formatée
|
|
129
|
-
}),
|
|
130
|
-
])
|
|
131
|
-
|
|
132
|
-
// Validité globale du composant (interne + toutes les règles supp)
|
|
133
|
-
const estValideComplet = computed(() => {
|
|
134
|
-
if (!estValide.value) return false
|
|
135
|
-
return props.reglesSupp.every(rule => rule(codeBudgetaire.value) === true)
|
|
136
|
-
})
|
|
87
|
+
const [code, nomBrut] = item
|
|
88
|
+
const nom = (nomBrut ?? '').toString().trim()
|
|
137
89
|
|
|
138
|
-
|
|
139
|
-
|
|
90
|
+
return { code, nom }
|
|
91
|
+
})
|
|
92
|
+
})
|
|
140
93
|
|
|
141
|
-
|
|
142
|
-
|
|
94
|
+
const placeholder = computed(() => {
|
|
95
|
+
const base = format.replace(/9/g, '0')
|
|
96
|
+
const extension = activerExtension ? '-XXX/XXX' : ''
|
|
97
|
+
return base + extension
|
|
98
|
+
})
|
|
143
99
|
|
|
144
|
-
|
|
145
|
-
|
|
100
|
+
const estValide = computed(() => {
|
|
101
|
+
const val = codeBudgetaire.value?.toUpperCase().trim() || ''
|
|
102
|
+
const base = val.slice(0, 15)
|
|
103
|
+
const extension = val.slice(15)
|
|
146
104
|
|
|
147
|
-
|
|
148
|
-
if (position < 15) {
|
|
149
|
-
if (!/^\d$/.test(e.key)) {
|
|
150
|
-
e.preventDefault()
|
|
151
|
-
return
|
|
152
|
-
}
|
|
105
|
+
if (!/^\d{3}-\d{1}-\d{5}-\d{3}$/.test(base)) return false
|
|
153
106
|
|
|
154
|
-
|
|
155
|
-
e.preventDefault()
|
|
107
|
+
if (!activerExtension) return val.length === 15
|
|
156
108
|
|
|
157
|
-
|
|
158
|
-
|
|
109
|
+
if (val.length === 15) return true
|
|
110
|
+
if (val.length !== 22) return false
|
|
111
|
+
if (extension.length !== 7) return false
|
|
159
112
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (clean.length > 3) formatted += '-' + clean.slice(3, 4)
|
|
163
|
-
if (clean.length > 4) formatted += '-' + clean.slice(4, 9)
|
|
164
|
-
if (clean.length > 9) formatted += '-' + clean.slice(9, 12)
|
|
113
|
+
if (extension[3] !== '/') return false
|
|
114
|
+
if (!/^[A-Z0-9/]$/i.test(extension[0]!)) return false
|
|
165
115
|
|
|
166
|
-
|
|
116
|
+
const alphanumAt = [1, 2, 4, 5, 6]
|
|
117
|
+
return alphanumAt.every(i => /^[A-Z0-9]$/i.test(extension[i]!))
|
|
118
|
+
})
|
|
167
119
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
120
|
+
// Règles Vuetify combinées (interne + supplémentaires)
|
|
121
|
+
const reglesVuetify = computed(() => [
|
|
122
|
+
// règle interne
|
|
123
|
+
() => (estValide.value ? true : props.regleMessageErreur),
|
|
172
124
|
|
|
173
|
-
|
|
174
|
-
|
|
125
|
+
// règles supplémentaires venant du parent
|
|
126
|
+
...props.reglesSupp.map(rule => {
|
|
127
|
+
return () => rule(codeBudgetaire.value) // on passe la valeur formatée
|
|
128
|
+
}),
|
|
129
|
+
])
|
|
175
130
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
131
|
+
// Validité globale du composant (interne + toutes les règles supp)
|
|
132
|
+
const estValideComplet = computed(() => {
|
|
133
|
+
if (!estValide.value) return false
|
|
134
|
+
return props.reglesSupp.every(rule => rule(codeBudgetaire.value) === true)
|
|
135
|
+
})
|
|
181
136
|
|
|
182
|
-
|
|
137
|
+
const caractereAutorises = (e: KeyboardEvent) => {
|
|
138
|
+
if (e.key === 'Enter') {
|
|
139
|
+
sauvegarder()
|
|
140
|
+
return
|
|
141
|
+
}
|
|
183
142
|
|
|
184
|
-
|
|
185
|
-
if (extensionPos === 0) {
|
|
186
|
-
if (!/^[A-Z0-9/]$/i.test(e.key)) {
|
|
187
|
-
e.preventDefault()
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
return
|
|
191
|
-
}
|
|
143
|
+
if (e.ctrlKey || e.metaKey) return
|
|
192
144
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (!/^[A-Z0-9]$/i.test(e.key)) {
|
|
196
|
-
e.preventDefault()
|
|
197
|
-
return
|
|
198
|
-
}
|
|
199
|
-
return
|
|
200
|
-
}
|
|
145
|
+
const touchesSpecifiques = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Home', 'End']
|
|
146
|
+
if (touchesSpecifiques.includes(e.key)) return
|
|
201
147
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
e.preventDefault()
|
|
205
|
-
const before = codeBudgetaire.value.slice(0, position)
|
|
206
|
-
const after = codeBudgetaire.value.slice(position)
|
|
207
|
-
codeBudgetaire.value = before + '/' + after
|
|
208
|
-
nextTick(() => {
|
|
209
|
-
input.selectionStart = input.selectionEnd = position + 1
|
|
210
|
-
})
|
|
211
|
-
return
|
|
212
|
-
}
|
|
148
|
+
const input = e.target as HTMLInputElement
|
|
149
|
+
const position = input.selectionStart ?? 0
|
|
213
150
|
|
|
214
|
-
|
|
151
|
+
// Gestion de la partie de base (15 premiers caractères)
|
|
152
|
+
if (position < 15) {
|
|
153
|
+
if (!/^\d$/.test(e.key)) {
|
|
215
154
|
e.preventDefault()
|
|
155
|
+
return
|
|
216
156
|
}
|
|
217
157
|
|
|
218
|
-
|
|
219
|
-
|
|
158
|
+
// Insérer chiffre et auto-ajout des tirets
|
|
159
|
+
e.preventDefault()
|
|
220
160
|
|
|
221
|
-
|
|
222
|
-
|
|
161
|
+
const value = codeBudgetaire.value.replace(/-/g, '')
|
|
162
|
+
const clean = value.slice(0, 12) + e.key
|
|
223
163
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
164
|
+
let formatted = ''
|
|
165
|
+
if (clean.length > 0) formatted += clean.slice(0, 3)
|
|
166
|
+
if (clean.length > 3) formatted += '-' + clean.slice(3, 4)
|
|
167
|
+
if (clean.length > 4) formatted += '-' + clean.slice(4, 9)
|
|
168
|
+
if (clean.length > 9) formatted += '-' + clean.slice(9, 12)
|
|
229
169
|
|
|
230
|
-
|
|
170
|
+
codeBudgetaire.value = formatted.slice(0, 15)
|
|
231
171
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// Ne garder que le premier slash s’il est à l’index 0 ou 3
|
|
238
|
-
ext = ext.filter((c, i) => {
|
|
239
|
-
if (c !== '/') return true
|
|
240
|
-
return i === 0 || i === 3
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
// Enlever les slash non autorisés
|
|
244
|
-
ext = ext.map((c, i) => {
|
|
245
|
-
if (c === '/' && i !== 0 && i !== 3) return ''
|
|
246
|
-
if (c !== '/' && !/^[A-Z0-9]$/i.test(c)) return ''
|
|
247
|
-
return c
|
|
248
|
-
})
|
|
172
|
+
nextTick(() => {
|
|
173
|
+
const newPos = codeBudgetaire.value.length
|
|
174
|
+
input.selectionStart = input.selectionEnd = newPos
|
|
175
|
+
})
|
|
249
176
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
ext[3] = '/'
|
|
253
|
-
}
|
|
177
|
+
return
|
|
178
|
+
}
|
|
254
179
|
|
|
255
|
-
|
|
256
|
-
|
|
180
|
+
// --- Gestion de l'extension ---
|
|
181
|
+
if (!activerExtension || position >= 22 || codeBudgetaire.value.length >= 22) {
|
|
182
|
+
e.preventDefault()
|
|
183
|
+
return
|
|
184
|
+
}
|
|
257
185
|
|
|
258
|
-
|
|
259
|
-
}
|
|
186
|
+
const extensionPos = position - 15
|
|
260
187
|
|
|
261
|
-
|
|
188
|
+
// Règle 1 : extension[0] = alphanum ou /
|
|
189
|
+
if (extensionPos === 0) {
|
|
190
|
+
if (!/^[A-Z0-9/]$/i.test(e.key)) {
|
|
262
191
|
e.preventDefault()
|
|
263
|
-
|
|
264
|
-
if (!clipboardData) return
|
|
265
|
-
let pasted = clipboardData.getData('text') || ''
|
|
266
|
-
codeBudgetaire.value = formaterCodeBudgetaire(pasted)
|
|
267
|
-
|
|
268
|
-
setTimeout(() => {
|
|
269
|
-
const input = e.target as HTMLInputElement
|
|
270
|
-
input.selectionStart = input.selectionEnd = codeBudgetaire.value.length
|
|
271
|
-
}, 0)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const sauvegarder = () => {
|
|
275
|
-
codeBudgetaire.value = formaterCodeBudgetaire(codeBudgetaire.value)
|
|
276
|
-
|
|
277
|
-
if (!estValideComplet.value) return
|
|
278
|
-
if (codeBudgetaire.value === derniereValeurSauvegardee.value) return
|
|
279
|
-
|
|
280
|
-
const codeNormalise = codeBudgetaire.value.trim().toUpperCase()
|
|
281
|
-
|
|
282
|
-
const existe = props.codeBudgetairesProp.some(item => {
|
|
283
|
-
const code = typeof item === 'string' ? item : item[0]
|
|
284
|
-
return code.trim().toUpperCase() === codeNormalise
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
if (!existe) {
|
|
288
|
-
const nouvelleListe = [...props.codeBudgetairesProp, codeBudgetaire.value]
|
|
289
|
-
emit('update:codeBudgetairesProp', nouvelleListe)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
derniereValeurSauvegardee.value = codeBudgetaire.value
|
|
293
|
-
emit('update:modelValue', codeBudgetaire.value)
|
|
192
|
+
return
|
|
294
193
|
}
|
|
194
|
+
return
|
|
195
|
+
}
|
|
295
196
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (typeof val === 'object' && 'code' in (val as Record<string, unknown>)) {
|
|
302
|
-
const obj = val as { code?: unknown }
|
|
303
|
-
return typeof obj.code === 'string' ? obj.code : String(obj.code ?? '')
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return String(val)
|
|
197
|
+
// Règle 2 : extension[1,2,4,5,6] = alphanum
|
|
198
|
+
if ([1, 2, 4, 5, 6].includes(extensionPos)) {
|
|
199
|
+
if (!/^[A-Z0-9]$/i.test(e.key)) {
|
|
200
|
+
e.preventDefault()
|
|
201
|
+
return
|
|
307
202
|
}
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Règle 3 : slash automatique à position 3 (index 18)
|
|
207
|
+
if (extensionPos === 3) {
|
|
208
|
+
e.preventDefault()
|
|
209
|
+
const before = codeBudgetaire.value.slice(0, position)
|
|
210
|
+
const after = codeBudgetaire.value.slice(position)
|
|
211
|
+
codeBudgetaire.value = before + '/' + after
|
|
212
|
+
nextTick(() => {
|
|
213
|
+
input.selectionStart = input.selectionEnd = position + 1
|
|
214
|
+
})
|
|
215
|
+
return
|
|
216
|
+
}
|
|
308
217
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const valeurFormatee = codeBudgetaire.value.trim().toUpperCase()
|
|
218
|
+
// Tout autre cas = bloqué
|
|
219
|
+
e.preventDefault()
|
|
220
|
+
}
|
|
314
221
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return codeItem.trim().toUpperCase() === valeurFormatee
|
|
318
|
-
})
|
|
222
|
+
const formaterCodeBudgetaire = (valeur: string): string => {
|
|
223
|
+
if (!valeur) return ''
|
|
319
224
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
225
|
+
const upper = valeur.toUpperCase().replace(/[^A-Z0-9/]/g, '')
|
|
226
|
+
const chiffres = upper.replace(/[^0-9]/g, '').slice(0, 12)
|
|
227
|
+
|
|
228
|
+
let base = ''
|
|
229
|
+
if (chiffres.length > 0) base += chiffres.slice(0, 3)
|
|
230
|
+
if (chiffres.length > 3) base += '-' + chiffres.slice(3, 4)
|
|
231
|
+
if (chiffres.length > 4) base += '-' + chiffres.slice(4, 9)
|
|
232
|
+
if (chiffres.length > 9) base += '-' + chiffres.slice(9, 12)
|
|
233
|
+
|
|
234
|
+
if (!activerExtension || base.length < 15) return base
|
|
235
|
+
|
|
236
|
+
const reste = upper.slice(chiffres.length).replace(/[^A-Z0-9/]/gi, '')
|
|
237
|
+
|
|
238
|
+
// Extraire les 7 premiers caractères restants pour l’extension
|
|
239
|
+
let ext = reste.slice(0, 7).split('')
|
|
240
|
+
|
|
241
|
+
// Ne garder que le premier slash s’il est à l’index 0 ou 3
|
|
242
|
+
ext = ext.filter((c, i) => {
|
|
243
|
+
if (c !== '/') return true
|
|
244
|
+
return i === 0 || i === 3
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
// Enlever les slash non autorisés
|
|
248
|
+
ext = ext.map((c, i) => {
|
|
249
|
+
if (c === '/' && i !== 0 && i !== 3) return ''
|
|
250
|
+
if (c !== '/' && !/^[A-Z0-9]$/i.test(c)) return ''
|
|
251
|
+
return c
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Forcer le slash à la 4e position
|
|
255
|
+
if (ext.length > 3) {
|
|
256
|
+
ext[3] = '/'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Réduire à 7 caractères
|
|
260
|
+
ext = ext.slice(0, 7)
|
|
261
|
+
|
|
262
|
+
return (base + ext.join('')).slice(0, 22)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const gererPaste = (e: ClipboardEvent) => {
|
|
266
|
+
e.preventDefault()
|
|
267
|
+
const clipboardData = e.clipboardData
|
|
268
|
+
if (!clipboardData) return
|
|
269
|
+
const pasted = clipboardData.getData('text') || ''
|
|
270
|
+
codeBudgetaire.value = formaterCodeBudgetaire(pasted)
|
|
271
|
+
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
const input = e.target as HTMLInputElement
|
|
274
|
+
input.selectionStart = input.selectionEnd = codeBudgetaire.value.length
|
|
275
|
+
}, 0)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const sauvegarder = () => {
|
|
279
|
+
codeBudgetaire.value = formaterCodeBudgetaire(codeBudgetaire.value)
|
|
280
|
+
|
|
281
|
+
if (!estValideComplet.value) return
|
|
282
|
+
if (codeBudgetaire.value === derniereValeurSauvegardee.value) return
|
|
283
|
+
|
|
284
|
+
const codeNormalise = codeBudgetaire.value.trim().toUpperCase()
|
|
285
|
+
|
|
286
|
+
const existe = props.codeBudgetairesProp.some(item => {
|
|
287
|
+
const code = typeof item === 'string' ? item : item[0]
|
|
288
|
+
return code.trim().toUpperCase() === codeNormalise
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
if (!existe) {
|
|
292
|
+
const nouvelleListe = [...props.codeBudgetairesProp, codeBudgetaire.value]
|
|
293
|
+
emit('update:codeBudgetairesProp', nouvelleListe)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
derniereValeurSauvegardee.value = codeBudgetaire.value
|
|
297
|
+
emit('update:modelValue', codeBudgetaire.value)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const extraireCode = (val: unknown): string => {
|
|
301
|
+
if (val == null) return ''
|
|
302
|
+
if (typeof val === 'string') return val
|
|
303
|
+
|
|
304
|
+
// Cas où Vuetify enverrait { code, label }
|
|
305
|
+
if (typeof val === 'object' && 'code' in (val as Record<string, unknown>)) {
|
|
306
|
+
const obj = val as { code?: unknown }
|
|
307
|
+
return typeof obj.code === 'string' ? obj.code : String(obj.code ?? '')
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return String(val)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const gererChangement = (val: unknown) => {
|
|
314
|
+
const code = extraireCode(val)
|
|
315
|
+
codeBudgetaire.value = formaterCodeBudgetaire(code)
|
|
316
|
+
|
|
317
|
+
const valeurFormatee = codeBudgetaire.value.trim().toUpperCase()
|
|
318
|
+
|
|
319
|
+
const estDansListe = props.codeBudgetairesProp.some(item => {
|
|
320
|
+
const codeItem = typeof item === 'string' ? item : item[0]
|
|
321
|
+
return codeItem.trim().toUpperCase() === valeurFormatee
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
if (
|
|
325
|
+
estDansListe &&
|
|
326
|
+
valeurFormatee !== (derniereValeurSauvegardee.value ?? '').toUpperCase() &&
|
|
327
|
+
estValideComplet.value
|
|
328
|
+
) {
|
|
329
|
+
sauvegarder()
|
|
330
|
+
}
|
|
331
|
+
}
|
|
328
332
|
|
|
329
333
|
watch(
|
|
330
334
|
() => codeBudgetaire.value,
|