|
|
@@ -10,7 +10,7 @@ Liste déroulante avec autocompletion
|
|
|
autocomplete="search"
|
|
|
:value="data"
|
|
|
:items="itemsToDisplayed"
|
|
|
- :label="$t(label)"
|
|
|
+ :label="$t(label_field)"
|
|
|
item-text="itemTextDisplay"
|
|
|
:item-value="itemValue"
|
|
|
:no-data-text="$t('autocomplete_research')"
|
|
|
@@ -22,6 +22,7 @@ Liste déroulante avec autocompletion
|
|
|
:search-input.sync="search"
|
|
|
:prepend-icon="prependIcon"
|
|
|
:error="error"
|
|
|
+ :rules="rules"
|
|
|
@input="onChange($event)"
|
|
|
>
|
|
|
<template v-if="slotText" v-slot:item="data">
|
|
|
@@ -32,7 +33,7 @@ Liste déroulante avec autocompletion
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts">
|
|
|
-import { computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted } from '@nuxtjs/composition-api'
|
|
|
+import {computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted, useContext} from '@nuxtjs/composition-api'
|
|
|
import { AnyJson } from '~/types/interfaces'
|
|
|
import * as _ from 'lodash'
|
|
|
import {$objectProperties} from "~/services/utils/objectProperties";
|
|
|
@@ -51,7 +52,7 @@ export default defineComponent({
|
|
|
default: null
|
|
|
},
|
|
|
data: {
|
|
|
- type: [String, Number, Object],
|
|
|
+ type: [String, Number, Object, Array],
|
|
|
required: false,
|
|
|
default: null
|
|
|
},
|
|
|
@@ -72,6 +73,11 @@ export default defineComponent({
|
|
|
type: Array,
|
|
|
required: true
|
|
|
},
|
|
|
+ group:{
|
|
|
+ type: String,
|
|
|
+ required: false,
|
|
|
+ default: null
|
|
|
+ },
|
|
|
slotText: {
|
|
|
type: Array,
|
|
|
required: false,
|
|
|
@@ -96,45 +102,91 @@ export default defineComponent({
|
|
|
prependIcon: {
|
|
|
type: String
|
|
|
},
|
|
|
+ translate: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ type: Array,
|
|
|
+ required: false,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
},
|
|
|
setup (props, { emit }) {
|
|
|
+ const {app:{i18n}} = useContext()
|
|
|
const search:Ref<string|null> = ref(null)
|
|
|
const {error, onChange} = $useError(props.field, emit)
|
|
|
|
|
|
- // On reconstruit les items à afficher car le text de l'Item doit être construit par rapport au itemText passé en props
|
|
|
+ // On reconstruit les items à afficher...
|
|
|
const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {
|
|
|
- return props.items.map((item: any) => {
|
|
|
- const slotTextDisplay: Array<string> = []
|
|
|
- const itemTextDisplay: Array<string> = []
|
|
|
+ const itemsByGroup:Array<Array<string>> = classItemsByGroup(props.items)
|
|
|
+ return prepareItemsToDisplayed(itemsByGroup)
|
|
|
+ })
|
|
|
+
|
|
|
+ const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
|
|
|
+ if(newResearch !== oldResearch && oldResearch !== null)
|
|
|
+ emit('research', newResearch)
|
|
|
+ }, 500))
|
|
|
+
|
|
|
+ onUnmounted(() => {
|
|
|
+ unwatch()
|
|
|
+ })
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * On construit l'Array à double entrée contenant les groups (headers) et les propositions
|
|
|
+ * @param items
|
|
|
+ */
|
|
|
+ const classItemsByGroup = (items:Array<any>): Array<Array<string>> => {
|
|
|
+ const itemsByGroup:Array<Array<string>> = []
|
|
|
+ for (const item of items){
|
|
|
if(item){
|
|
|
+ if(!itemsByGroup[item[props.group as string]])
|
|
|
+ itemsByGroup[item[props.group as string]] = []
|
|
|
+
|
|
|
+ itemsByGroup[item[props.group as string]].push(item)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return itemsByGroup
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Construction de l'Array JSON contenant toutes les propositions à afficher dans le select
|
|
|
+ * @param itemsByGroup
|
|
|
+ */
|
|
|
+ const prepareItemsToDisplayed = (itemsByGroup:Array<Array<string>>):Array<AnyJson> => {
|
|
|
+ let finalItems:Array<AnyJson> = []
|
|
|
+ for(const group in itemsByGroup){
|
|
|
+
|
|
|
+ //Si un group est présent, alors on créer le group options header
|
|
|
+ if(group !== 'undefined'){
|
|
|
+ finalItems.push({header: i18n.t(group as string)})
|
|
|
+ }
|
|
|
+
|
|
|
+ //On parcours les items pour préparer les texts/slotTexts à afficher
|
|
|
+ finalItems = finalItems.concat(itemsByGroup[group].map((item: any) => {
|
|
|
+ const slotTextDisplay: Array<string> = []
|
|
|
+ const itemTextDisplay: Array<string> = []
|
|
|
+
|
|
|
item = $objectProperties.cloneAndFlatten(item)
|
|
|
|
|
|
//Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
|
|
|
if(props.slotText){
|
|
|
for (const text of props.slotText) {
|
|
|
- slotTextDisplay.push(item[text as string])
|
|
|
+ slotTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for (const text of props.itemText) {
|
|
|
- itemTextDisplay.push(item[text as string])
|
|
|
+ itemTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- //On reconstruit l'objet
|
|
|
- return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
|
|
|
- if(newResearch !== oldResearch && oldResearch !== null)
|
|
|
- emit('research', newResearch)
|
|
|
- }, 500))
|
|
|
-
|
|
|
- onUnmounted(() => {
|
|
|
- unwatch()
|
|
|
- })
|
|
|
+ //On reconstruit l'objet
|
|
|
+ return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ return finalItems
|
|
|
+ }
|
|
|
|
|
|
return {
|
|
|
label_field: props.label ?? props.field,
|