@tagplus/components 0.2.95 → 0.2.99
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/dist/tp.common.js +1023 -537
- package/dist/tp.common.js.map +1 -1
- package/dist/tp.css +1 -1
- package/dist/tp.umd.js +1023 -537
- package/dist/tp.umd.js.map +1 -1
- package/dist/tp.umd.min.js +1 -1
- package/dist/tp.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Autosuggest/Autosuggest.vue +37 -1
- package/src/components/AutosuggestTest/AutosuggestTest.vue +45 -0
- package/src/components/AutosuggestTest/autosuggest.js +291 -0
- package/src/components/Multisuggest/Multisuggest.vue +4 -2
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
v-clickoutside="handleClose"
|
|
4
4
|
class="el-select tp-autosuggest"
|
|
5
|
+
data-is-search="true"
|
|
5
6
|
:class="[selectSize ? 'el-select--' + selectSize : '']"
|
|
6
7
|
@click.stop="toggleMenu"
|
|
7
8
|
>
|
|
@@ -127,7 +128,7 @@
|
|
|
127
128
|
:class="{'created': item.created}"
|
|
128
129
|
>
|
|
129
130
|
<template v-if="item.created">
|
|
130
|
-
<a :id="`${_id}-btn-create`"><i class="far fa-plus" />
|
|
131
|
+
<a :id="`${_id}-btn-create`"><i class="far fa-plus" /> {{ newItem }}</a>
|
|
131
132
|
</template>
|
|
132
133
|
<slot
|
|
133
134
|
v-else
|
|
@@ -263,6 +264,15 @@ export default {
|
|
|
263
264
|
remoteMethod: {
|
|
264
265
|
type: Function,
|
|
265
266
|
required: true
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Informa a mensagem de cadastrar novo item
|
|
271
|
+
*/
|
|
272
|
+
msgNewItem: {
|
|
273
|
+
type: String,
|
|
274
|
+
required: false,
|
|
275
|
+
default: 'Cadastrar novo item'
|
|
266
276
|
}
|
|
267
277
|
},
|
|
268
278
|
|
|
@@ -276,6 +286,9 @@ export default {
|
|
|
276
286
|
_id() {
|
|
277
287
|
return this.id || this.$options.name
|
|
278
288
|
},
|
|
289
|
+
newItem(){
|
|
290
|
+
return this.query ? this.$tpI18n.t('autosuggests.cadastrar', {nameItem: this.query}) : this.$tpI18n.t(`autosuggests.newItem.${this.$parent.$options.name}`)
|
|
291
|
+
},
|
|
279
292
|
|
|
280
293
|
/**
|
|
281
294
|
* Trata o caso que o autosuggest recebe um objeto com o valueKey e labelKey
|
|
@@ -371,9 +384,18 @@ export default {
|
|
|
371
384
|
this.createHandler(componenteInputInterno, 'mouseleave', () => {
|
|
372
385
|
this.tooltipVisible = false
|
|
373
386
|
})
|
|
387
|
+
document.addEventListener('focusin', this.fixElSelect)
|
|
388
|
+
document.addEventListener('click', this.fixElSelect)
|
|
389
|
+
document.addEventListener('touchstart', this.fixElSelect)
|
|
374
390
|
})
|
|
375
391
|
},
|
|
376
392
|
|
|
393
|
+
beforeDestroy() {
|
|
394
|
+
document.removeEventListener('focusin', this.fixElSelect, false)
|
|
395
|
+
document.removeEventListener('click', this.fixElSelect, false)
|
|
396
|
+
document.removeEventListener('touchstart', this.fixElSelect, false)
|
|
397
|
+
},
|
|
398
|
+
|
|
377
399
|
methods: {
|
|
378
400
|
/**
|
|
379
401
|
* Adiciona um evento no elemento dinamicamente
|
|
@@ -587,8 +609,22 @@ export default {
|
|
|
587
609
|
) {
|
|
588
610
|
this.checkDefaultFirstOption()
|
|
589
611
|
}
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
// Correção para habilitar teclado mobile em iOS
|
|
615
|
+
fixElSelect() {
|
|
616
|
+
document.querySelectorAll('.el-select[data-is-search="true"]:hover').forEach(() => {
|
|
617
|
+
let elInput = document.querySelector('.el-select[data-is-search="true"]:hover input[readonly]')
|
|
618
|
+
if (elInput) {
|
|
619
|
+
elInput.readOnly = false
|
|
620
|
+
elInput.blur()
|
|
621
|
+
elInput.focus()
|
|
622
|
+
}
|
|
623
|
+
})
|
|
590
624
|
}
|
|
591
625
|
}
|
|
626
|
+
|
|
627
|
+
|
|
592
628
|
}
|
|
593
629
|
</script>
|
|
594
630
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div>Autosuggest 1</div>
|
|
4
|
+
<tp-autosuggest
|
|
5
|
+
:id="_id"
|
|
6
|
+
:value="value"
|
|
7
|
+
:placeholder="$t($options.placeholder)"
|
|
8
|
+
:label-key="$options.labelKey"
|
|
9
|
+
:remote-method="autosuggestMixin_getList"
|
|
10
|
+
:suggestions="list"
|
|
11
|
+
:loading="loading"
|
|
12
|
+
:load-on-create="isLoadOnCreate"
|
|
13
|
+
:disabled="_disabled"
|
|
14
|
+
:allow-create="false"
|
|
15
|
+
@input="autosuggestMixin_input"
|
|
16
|
+
@handleOptionClick="autosuggestMixin_handleCreate"
|
|
17
|
+
@change="autosuggestMixin_handleChange"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
import AutosuggestsMixin from './autosuggest'
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
valueKey: 'id',
|
|
27
|
+
labelKey: 'name',
|
|
28
|
+
placeholder: 'common.placeholder',
|
|
29
|
+
|
|
30
|
+
mixins: [AutosuggestsMixin],
|
|
31
|
+
|
|
32
|
+
data() {
|
|
33
|
+
return {
|
|
34
|
+
params: {
|
|
35
|
+
uri: 'https://jsonplaceholder.typicode.com/users',
|
|
36
|
+
suggestions: [],
|
|
37
|
+
loading: true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<style lang="scss" scoped>
|
|
45
|
+
</style>
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mixin contendo metodos para autosuggests
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
props: {
|
|
6
|
+
/**
|
|
7
|
+
* @vuese
|
|
8
|
+
* id do css para teste unitário
|
|
9
|
+
*/
|
|
10
|
+
id: {
|
|
11
|
+
type: String
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
value: {
|
|
15
|
+
type: [Number, String, Object, Boolean, Array],
|
|
16
|
+
default: ''
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
// Desativa o loader global ao carregar lista
|
|
20
|
+
disableLoader: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
default: false
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// Indica se primeiro item da lista deve ser retornado por padrão
|
|
26
|
+
noDefault: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: false
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Se a lista vai ser carregada no create ou o labelKey não venha por padrão na requisição loadOnCreate = true,
|
|
33
|
+
* caso deva ser carregada quando o usuário clica deixar vazio ou false.
|
|
34
|
+
*/
|
|
35
|
+
loadOnCreate: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: null
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Indicia se o campo deve ser desabilitado
|
|
41
|
+
disabled: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: false
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Se o valor desse autosuggest for obrigatório se o value for vazio o after get vai selecionar a primeira opção
|
|
47
|
+
isRequired: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
allowCreate: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: true
|
|
55
|
+
},
|
|
56
|
+
valueKey: {
|
|
57
|
+
type: String,
|
|
58
|
+
default: 'id'
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
data () {
|
|
63
|
+
return {
|
|
64
|
+
header: {
|
|
65
|
+
'X-Skip-Total-Pages': true
|
|
66
|
+
},
|
|
67
|
+
params: {}
|
|
68
|
+
},
|
|
69
|
+
doEmptyRequest: false,
|
|
70
|
+
list: [],
|
|
71
|
+
loading: true,
|
|
72
|
+
isLoadOnCreate: true,
|
|
73
|
+
modal: false,
|
|
74
|
+
autosuggest: false, // endpoint /autosuggest/*
|
|
75
|
+
customEndpoint: false // buscar em qualquer endpoint
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
computed: {
|
|
80
|
+
_id() {
|
|
81
|
+
return this.id || this.$options.name
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/** desabilita o componente quando recebe disable true ou não tem permissão no form de incluir ou alterar */
|
|
85
|
+
_disabled() {
|
|
86
|
+
if (this.disabled) {
|
|
87
|
+
return true
|
|
88
|
+
} else if (this.disableForm) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
return false
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/** @vuese
|
|
95
|
+
* Action para buscar em stores customizadas/dinâmicas, por padrão pega do $options.listAction
|
|
96
|
+
*/
|
|
97
|
+
listAction() {
|
|
98
|
+
return this.customAction ? this.customAction : this.$options.listAction
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/** @vuese
|
|
102
|
+
* Retorna a URL a ser usada para buscar a lista caso não esteja definida no $options
|
|
103
|
+
* Passam aqui as urls customizadas, do /autosuggests, /entidade/categorias etc
|
|
104
|
+
*/
|
|
105
|
+
customUrl() {
|
|
106
|
+
if (this.customEndpoint) {
|
|
107
|
+
return this.customEndpoint
|
|
108
|
+
} else if (this.autosuggest) {
|
|
109
|
+
return `/autosuggests/${this.$options.autosuggest}`
|
|
110
|
+
} else {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
_valueKey() {
|
|
116
|
+
return this.$options.valueKey || 'id'
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
_labelKey() {
|
|
120
|
+
return this.$options.labelKey || 'descricao'
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Ao montar componente, inicia o loading
|
|
126
|
+
*/
|
|
127
|
+
mounted() {
|
|
128
|
+
if (!this.disableLoader && this.isLoadOnCreate) {
|
|
129
|
+
this.startLoading(this.$options.name)
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
created() {
|
|
134
|
+
if (this.$parent.$options.name.substring(0, 6) === 'Filtro') {
|
|
135
|
+
this.isLoadOnCreate = false
|
|
136
|
+
} else if (this.loadOnCreate !== null) { // Se recebeu loadOnCreate via prop leva precedência
|
|
137
|
+
// Se for campo obrigatório e não vier o value inicial força a busca na api
|
|
138
|
+
if (this.isRequired && typeof this.value === 'undefined') {
|
|
139
|
+
this.isLoadOnCreate = true
|
|
140
|
+
} else {
|
|
141
|
+
this.isLoadOnCreate = this.loadOnCreate
|
|
142
|
+
}
|
|
143
|
+
} else if (this.$route && !isNaN(this.$route.params.id)) {
|
|
144
|
+
// Se está num form com ID só carrega lista quando clica
|
|
145
|
+
this.isLoadOnCreate = false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Caso tenha value no created a primeira busca quando clica é vazia
|
|
149
|
+
this.doEmptyRequest = (typeof this.value !== 'undefined')
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
methods: {
|
|
153
|
+
/**
|
|
154
|
+
* HOOK executado quando getList é chamado
|
|
155
|
+
*/
|
|
156
|
+
autosuggestMixin_beforeGet() { },
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* HOOK executado quando getList é chamado
|
|
160
|
+
*/
|
|
161
|
+
autosuggestMixin_afterGet() { },
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* HOOK executado quando handleClose é chamado
|
|
165
|
+
*/
|
|
166
|
+
autosuggestMixin_beforeHandleClose() { },
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Realiza chamada da action no endpoint
|
|
170
|
+
* @param {*} busca valor recebido no autosuggest
|
|
171
|
+
*/
|
|
172
|
+
async autosuggestMixin_getList(busca) {
|
|
173
|
+
busca ||= ''
|
|
174
|
+
|
|
175
|
+
if (!Array.isArray(busca) && (busca !== '' || this.config.params.q !== busca)) {
|
|
176
|
+
this.config.params.q = busca
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Tratamento caso clique no primeiro autosuggest daquele tipo com loadOnCreate false e tenha um valor padrão
|
|
180
|
+
if (this.doEmptyRequest) {
|
|
181
|
+
this.config.params.q = ''
|
|
182
|
+
this.doEmptyRequest = false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Hook executado antes de recuperar a lista
|
|
186
|
+
this.autosuggestMixin_beforeGet()
|
|
187
|
+
|
|
188
|
+
this.loading = true
|
|
189
|
+
|
|
190
|
+
// Se buscar em /autosuggest ou url dinâmica/customizada pega a url aqui
|
|
191
|
+
this.config.customUrl = this.customUrl
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
await this.axios
|
|
195
|
+
.get(`${this.params.uri}?q=${busca}`)
|
|
196
|
+
.then(res => {
|
|
197
|
+
this.list = res.data
|
|
198
|
+
})
|
|
199
|
+
// eslint-disable-next-line no-unused-vars
|
|
200
|
+
.catch(err => {
|
|
201
|
+
this.list = []
|
|
202
|
+
}).finally(() => {
|
|
203
|
+
this.loading = false
|
|
204
|
+
})
|
|
205
|
+
} catch (err) {
|
|
206
|
+
// this.$TpError(err)
|
|
207
|
+
} finally {
|
|
208
|
+
// this.finishLoading(this.$options.name)
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Metodo para lidar com a criação de um novo registro
|
|
214
|
+
* Exibe o modal por padrão
|
|
215
|
+
* @param {*} evt Evento de criação
|
|
216
|
+
*/
|
|
217
|
+
autosuggestMixin_handleCreate(evt) {
|
|
218
|
+
if (evt.created) {
|
|
219
|
+
this.modal = true
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Método para lidar com o fechamento do modal
|
|
225
|
+
* Por padrão recarrega a lista e recupera o valor criado no modal, caso haja
|
|
226
|
+
* @param {*} val Valor recebido do modal
|
|
227
|
+
*/
|
|
228
|
+
async autosuggestMixin_handleClose(val) {
|
|
229
|
+
// Fecha o modal primeiro para diminuir tempo de transição
|
|
230
|
+
this.modal = false
|
|
231
|
+
|
|
232
|
+
if (val.id !== 0) {
|
|
233
|
+
const id = val.id?.id !== undefined ? val.id.id : val.id
|
|
234
|
+
|
|
235
|
+
await this.autosuggestMixin_getList('')
|
|
236
|
+
|
|
237
|
+
this.$emit('input', val.id)
|
|
238
|
+
|
|
239
|
+
const item = this.list.filter((value) => {
|
|
240
|
+
return id === value.id
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
this.$emit('change', item[0])
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Evento para tratamento do valor
|
|
249
|
+
* Devolve o valor recebido para o v-model no componente pai
|
|
250
|
+
* @param {*} evt
|
|
251
|
+
*/
|
|
252
|
+
autosuggestMixin_input(evt) {
|
|
253
|
+
this.$emit('input', evt)
|
|
254
|
+
this.autosuggestMixin_inputFullItem(evt)
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Evento Input com os dados completos do item selecionado
|
|
259
|
+
* @param {*} evt
|
|
260
|
+
*/
|
|
261
|
+
autosuggestMixin_inputFullItem(evt) {
|
|
262
|
+
let res = []
|
|
263
|
+
|
|
264
|
+
if (Array.isArray(evt)) {
|
|
265
|
+
evt.map((idValue) => {
|
|
266
|
+
this.list.map((item2) => {
|
|
267
|
+
if (idValue === item2[this._valueKey]) {
|
|
268
|
+
res.push(item2)
|
|
269
|
+
}
|
|
270
|
+
return item2
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
} else {
|
|
274
|
+
res = evt
|
|
275
|
+
}
|
|
276
|
+
this.$emit('inputFullItem', res)
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
autosuggestMixin_handleChange(val) {
|
|
280
|
+
if (val !== null && val !== undefined) {
|
|
281
|
+
const item = this.list.filter((value) => {
|
|
282
|
+
return val === value.id
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
this.$emit('change', item[0])
|
|
286
|
+
} else {
|
|
287
|
+
this.$emit('change', val)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -459,8 +459,10 @@ export default {
|
|
|
459
459
|
|
|
460
460
|
this.value.forEach((item,key) => {
|
|
461
461
|
if (item[this.valueKey]) {
|
|
462
|
-
this.selectedLabelsArray.push(item)
|
|
463
462
|
this.value[key] = item[this.valueKey]
|
|
463
|
+
if(item[this.labelKey]) {
|
|
464
|
+
this.selectedLabelsArray.push(item)
|
|
465
|
+
}
|
|
464
466
|
}
|
|
465
467
|
})
|
|
466
468
|
|
|
@@ -523,7 +525,7 @@ export default {
|
|
|
523
525
|
else if (val && typeof val === 'object') {
|
|
524
526
|
// Se val for Object converte para outro tipo
|
|
525
527
|
if (!val[this.valueKey]) {
|
|
526
|
-
throw new TypeError('
|
|
528
|
+
throw new TypeError('Multisuggest option doesn\'t have a valueKey \'' + this.valueKey + '\' key')
|
|
527
529
|
} else {
|
|
528
530
|
// Se mandou a label no objeto value
|
|
529
531
|
if (val[this.labelKey]) {
|