# File picker Bone
# Introducción
Componente lógico.
# Código fuente
<template>
<ir-file-picker-bone
v-bind="$attrs"
v-model="localValue"
:file="file"
@input="changeValue"
@input-base64="changebase64"
@update:link="updateLinkValue"
>
<div
slot-scope="{
imgEvents,
fileName,
areAllImages,
fileSetted,
loading,
inputEvents,
required,
removeEvents,
upload
}"
>
<ir-image-gallery
v-if="fileName && fileSetted && preview && areAllImages && !loading"
:images="getFilesLinks(fileName)"
:img-events="imgEvents"
/>
<div
v-if="fileSetted && preview && !loading && url && !areAllImages"
style="position: relative"
>
<template v-if="!localUrl">
<a
v-for="file in fileName"
:key="file"
:href="`${url}${file}`"
target="_blank"
>
<v-btn
class="mr-2 mb-2"
outlined
rounded
color="primary"
download
>
{{ filenameShort(file.split('/').pop()) }}
</v-btn>
</a>
</template>
<template v-else>
<a
v-for="fileIterado in fileArray"
:key="fileIterado.name"
:href="getFileLink(fileIterado)"
target="_blank"
>
<v-btn
class="mr-2 mb-2"
outlined
rounded
color="primary"
download
>
{{ filenameShort(fileIterado.name.split('/').pop()) }}
</v-btn>
</a>
</template>
</div>
<div class="d-flex">
<v-file-input
:ref="irFilePickerRef"
v-model="file.file"
data-cy="filepicker"
prepend-icon="mdi-paperclip"
:loading="loading"
:clearable="false"
v-bind="getFilteredProps"
:rules="
required == true
? [
(v) =>
(v && (v.length > 0 || fileName != '')) ||
'Complete este campo'
]
: []
"
:hide-details="false"
v-on="inputEvents"
@change="(e) => setLocalPreview(e, upload)"
>
<template #message="{ message }">
<div v-html="message" />
</template>
</v-file-input>
<div
class="d-flex tw-align-center tw-cursor-pointer"
v-on="removeEvents"
>
<v-icon>mdi-delete</v-icon>
</div>
</div>
</div>
</ir-file-picker-bone>
</template>
<script>
import IrFilePickerBone from './IrFilePickerBone.vue'
import IrImageGallery from '../../IrImageGallery/IrImageGallery.vue'
import { filePickerComponentName } from '../../../constants'
import { setHintTag } from '../../../helpers/hint'
import { getBaseName } from '../../../helpers/getFilesBaseNames'
import focusAndScrollMixin from '../../../mixins/focusAndScroll'
import { filterProps } from '../../../helpers/filterProps'
export default {
name: filePickerComponentName,
components: {
IrFilePickerBone,
IrImageGallery
},
mixins: [focusAndScrollMixin],
inheritAttrs: false,
props: {
preview: {
type: Boolean,
default: false
},
value: { type: Array, required: true },
url: { type: String, required: false, default: undefined },
saveDraft: { type: Object, required: false, default: undefined },
linked: { type: Boolean, required: false, default: false },
isFocused: {
type: Boolean,
default: false
},
hint: {
type: [String, Object],
default: ''
},
link: { type: Array, required: false, default: null }
},
data () {
return {
irFilePickerRef: filePickerComponentName,
file: { file: [] },
localValue: this.value,
otherProps: { ...this.$attrs },
localUrl: false,
currentFiles: null
}
},
computed: {
getFilteredProps () {
return filterProps(this.$refs[this.irFilePickerRef], this.handledAttrs, [
'data-cy'
])
},
fileArray () {
//Retorna siempre un arreglo de archivos para poder iterarlo
if (Array.isArray(this.file.file)) return this.file.file
else return [this.file.file]
},
handledAttrs () {
// Elimina attrs que son válidos para el componente de vuetify, pero que son manejados por el componente IR
const hint = setHintTag(this.otherProps, this.hint) //from hintMaker mixin
let aux = { ...this.otherProps, ...this.$attrs }
delete aux.hint
if (hint) {
aux = { ...aux, hint }
}
delete aux.value
delete aux.required
delete aux.readonly
delete aux.preview
delete aux.isImage
// delete aux.hint in hintMaker mixin
return aux
}
},
watch: {
value (isValue) {
//convierte value para v-model de v-file-input de tipo File
this.localValue = isValue
let blobPreload = []
if (Array.isArray(isValue)) {
isValue.forEach((file) => {
let blob = new File([], file)
blobPreload.push(blob)
})
}
this.file.file = isValue ? blobPreload : null
}
},
mounted () {
if (this.isFocused) {
let element = this.$refs[this.irFilePickerRef].$el.querySelector('input')
this.focusAndScroll(element)
}
this.localValue = this.value
//Inicializacion de value para file input de tipo File
let blobPreload = []
if (Array.isArray(this.value)) {
this.value.forEach((file) => {
let blob = new File([], file)
blobPreload.push(blob)
})
}
this.file.file = this.value ? blobPreload : null
this.otherProps = this.$attrs // Se asigna en mounted para inicializar que funcione hintMaker y volver a calcular handledAttrs
},
methods: {
getFilesLinks (fileName) {
//Genera los links de los archivos dependiendo si son preview locales o value obtenido de BD
if (this.localUrl) {
return [this.localUrl]
} else {
return fileName.map((file) => `${this.url}${file}`)
}
},
getFileLink (file) {
//Retorna la url local de un archivo
return URL.createObjectURL(file)
},
setLocalPreview (archivos, upload) {
// Normalizar ambos a arrays
const currentFilesArray = Array.isArray(this.currentFiles)
? this.currentFiles
: [this.currentFiles]
const archivosArray = Array.isArray(archivos) ? archivos : [archivos]
// Comparar si hay alguna diferencia en los nombres de archivo
const areDifferent =
archivosArray.length !== currentFilesArray.length ||
archivosArray.some(
(file, index) =>
getBaseName(file) !== getBaseName(currentFilesArray[index])
)
// Si todos los archivos son iguales, no continuar
if (!areDifferent) return
this.currentFiles = archivosArray
//determina la URL par los archivos locales, tanto para uno o multiples
if (!archivos) return
if (Array.isArray(archivos))
this.localUrl = archivos.map((file) => URL.createObjectURL(file))
else this.localUrl = URL.createObjectURL(archivos)
upload()
},
changeValue (v) {
if (Array.isArray(v) && v.length > 0) {
this.$emit('input', v)
if (this.linked === true) this.$emit('change-linked-value')
if (typeof this.saveDraft !== 'undefined')
this.$emit('saveDraft', this.saveDraft)
} else {
//on clear input se emite value array vacío
this.file.file = null
this.$emit('input', [])
}
},
changebase64 (value) {
this.$emit('update-prop', { prop: 'base64', value })
},
updateLinkValue (value) {
if (Array.isArray(this.link)) {
if (Array.isArray(value)) {
const newLinks = value.map((file) => `${this.url}${file}`)
this.$emit('update-prop', { prop: 'link', value: newLinks })
} else {
this.$emit('update-prop', { prop: 'link', value: [`${this.url}${value}`] })
}
}
},
filenameShort (filename) {
//Acorta los nombre de los archivos
let ext = filename.split('.').pop()
if (filename.length > 10) {
return filename.slice(0, 20) + '...' + ext
}
}
}
}
</script>