Как реализовать свой собственный компонент загрузки файла и сделать его настраиваемым в vue js? - PullRequest
0 голосов
/ 05 июля 2019

Я работаю над проектом, который ожидает много мест, где я должен реализовать компонент загрузки файла с разными стилями.Я хочу создать настраиваемый компонент, дизайн которого я могу легко изменить, а также я не хочу повторяться и хочу извлечь всю общую логику в одном месте.

На данный момент у меня есть vue.jsверсия 2.2.2 и фреймворк bulma css.У меня есть базовая реализация этого компонента с одним доступным дизайном.Он поддерживает несколько состояний, представляющих текущий статус загрузки:

  1. компонент ожидает ввода
  2. загрузка начата
  3. загрузка завершена успешно
  4. загрузкане удалось

Также этот компонент отвечает за процесс загрузки.

В этом мой компонент имеет много обязанностей: 1. Он знает, как обращаться со статусами:

<p v-if="isWaiting">
...
</p>

<div v-if="isLoading" class="is-loading">
    <p class="title">{{currentPercent}} %</p>
</div>

...
data() { return {
    currentStatus = Statuses.waiting
}}

computed: {
    isWaiting() {
      return this.currentStatus === Statuses.waiting;
    },
    ...
}
знает, как загрузить данные и подсчитать текущий процент уже переданных данных:
selectedFileChanged: async function(event) {
      if (event.target.files.length === 0) return;

      this.currentStatus = Statuses.uploading;
      this.selectedFile = event.target.files[0];

      const formData = new FormData();
      formData.append("file", this.selectedFile);

      try {
        const result = (await axios.post("some url", formData, {
          onUploadProgress: progress => {
            const loaded = progress.loaded;
            const total = progress.total;
            this.currentPercent = Math.floor((loaded * 100) / total);
          }
        })).data;

        this.currentStatus = Statuses.uploaded;

        this.$emit("fileUploaded", {
          file: this.selectedFile,
          payload: result
        });
      } catch (exception) {
        this.currentStatus = Statuses.error;
      }
    }
у него есть только один стиль, который я могу использовать

полный код вы можете найти здесь: https://codesandbox.io/s/vue-template-gz2gk

Поэтому мой вопрос: как создать этот компонент, чтобы иметьвозможность легко изменить свой стиль и как справляться со статусами загрузки?

Мне кажется, что: 1. компонент не должен знать, что такое axios и как загружать данные;2. компонент должен отвечать только за текущий статус и как его отображать

Я могу представить новый сервис загрузки, который будет знать, как загружать данные и добавлять новый реквизит (текущий статус) для файла загрузкикомпонент и изменить его из родительского компонента.Но в этом случае я напишу один и тот же код для всех экземпляров компонента.

Кто-нибудь знает лучшие практики создания такого настраиваемого компонента?

ОБНОВЛЕНИЕ 1 Я пытался реализовать эту функцию, используя слоты, и в итоге получил: https://codesandbox.io/s/vue-template-pi7e9

Компонент все еще знает, как загрузить данные, но теперь я могу изменить стиль загрузки компонента.Итак, новый вопрос: как работать со слотами и не передавать много переменных, и как бороться с загрузкой.Я не хочу, чтобы мой компонент знал, как загрузить данные: (

Ответы [ 2 ]

0 голосов
/ 05 июля 2019

Я закончил компонент следующим образом:

В моем родительском компоненте у меня есть два разных стиля для этого компонента:

  <div id="app" class="container box">
    <upload-file url="url"></upload-file> <-- the default one with statuses outside -->

    <upload-file url="url"> <-- custom one which look like a box with statuses in it -->
      <template #fileselect="{status, change}">
        <custom-file-upload :status="status" @change="change"></custom-file-upload>
      </template> <-- I've used custom-file-upload component here and tnjected all the properties from default implementation -->
    </upload-file>
  </div>

Мой ввод файла по умолчанию - не что иное, какслот с реализацией по умолчанию:

<template>
  <div>
    <slot name="fileselect" :change="selectedFileChanged" :status="status">
      <input id="fileselect" @change="selectedFileChanged" class="file-input" type="file">

      <div class="help is-info" v-if="isWaiting">Waiting</div>
      <div class="help is-success" v-if="isUploaded">Uploaded</div>
      <div class="help is-info" v-if="isUploading">Uploading {{currentPercent}} %</div>
      <div class="help is-error" v-if="isFailed">Failed</div>
    </slot>
  </div>
</template>

и как выглядит код:

  name: "upload-file",

  props: ["url"], // url which will be used in upload request

  data() {
    return {
      currentStatus: 1,
      selectedFile: null,
      currentPercent: 0
    };
  },

computed: {
    someOtherProperties,
    status() {
      return {
        isWaiting: this.isWaiting, // this.currentStatus === 1
        isUploaded: this.isUploaded,
        isUploading: this.isUploading,
        isFailed: this.isFailed,

        currentPercent: this.currentPercent,
        selectedFile: this.selectedFile
      };
    }
  },

 methods: {
    selectedFileChanged: function(event) {
      this.selectedFile = event.target.files[0];

      const xhr = new XMLHttpRequest();

      // some handlers for XHR

      xhr.open("POST", this.url, true);

      xhr.send(formData);
    }
  }

Теперь я могу использовать компонент загрузки файлов с другим стилем, но он будет инкапсулирован в базовую реализациювся логика обработки статуса и загрузки.

Я надеюсь, что это решение кому-то поможет :)

Чтобы просмотреть полную версию кода, пожалуйста, следуйте https://codesandbox.io/s/vue-template-pi7e9

0 голосов
/ 05 июля 2019

Самый востребованный способ - я думаю,


это Слоты


Из вашего примера:

<input
   id="fileselect"
   type="file"
   @change="selectedFileChanged"
   accept=".docx, .xlsx, .html, .htm, .pdf"
   class="input-file"
>

Вы можете преобразовать это в:

<slot name="fileselect">
  <input
    id="fileselect"
    type="file"
    @change="selectedFileChanged"
    accept=".docx, .xlsx, .html, .htm, .pdf"
    class="input-file"
  >
</slot>

, а затем - при условии, что ваши компоненты называются upload-file :

<upload-file>
  <template #fileselect >
    <custom-input-el @change="change" class="override-everything"/>
  </template>
</upload-file>

<script>export default {methods:{change(){}}}</script>
...