# 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>
Last Updated: 4/5/2024, 4:52:19 PM