欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

實(shí)現(xiàn)一個(gè)Vue版Upload組件

 更新時(shí)間:2022年08月15日 08:53:15   作者:Fone  
這篇文章主要介紹了實(shí)現(xiàn)一個(gè)Vue版Upload組件,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言

之前對(duì)一些主流手機(jī)拍出的照片大小做過(guò)對(duì)比,華為P30拍出的照片3M左右,同事的小米9不知開(kāi)啟了什么模式拍出了10M以上的照片。照片太大了對(duì)服務(wù)端上傳文件造成了不小的壓力,對(duì)此,后端對(duì)前端提出了圖片上傳前對(duì)圖片進(jìn)行壓縮。我們目前所用的UI庫(kù)Upload組件并不支持對(duì)上傳的圖片進(jìn)行壓縮,所以花了一點(diǎn)時(shí)間自己寫了上傳的組件。

今天分享我的第N個(gè)Vue組件,Upload

1.組件設(shè)計(jì)

  • 只有圖片裁進(jìn)行壓縮,文件沒(méi)法壓縮。
  • 超過(guò)規(guī)定大小的圖片裁進(jìn)行壓縮,不應(yīng)該是個(gè)圖片就壓縮,內(nèi)存過(guò)下的圖片沒(méi)必要壓縮。
  • 自行定義圖片壓縮的的寬度,高度按比例自動(dòng)適配。
  • Upload組件的禁用,該有的基本功能。
  • 文件上傳進(jìn)度。
  • 可接管文件上傳。
  • 等等圍繞以上幾個(gè)點(diǎn)開(kāi)始擴(kuò)展。

組件實(shí)現(xiàn)

1.mixins

export default {
    props: {
        icon: {      //上傳組件的占位圖
            type: String,
            default: "iconcamera"
        },
        size: {     //圖片超過(guò)指定大小不讓上傳
            type: Number,
            default: 3072
        },
        disabled: {  //禁止上傳
            type: Boolean
        },
        iconSize: {   //占位icon的大小
            type: Number,
            default: 24
        },
        name: {      //input的原生屬性
            type: String,
            default: 'file'
        },
        accept: {   //接受上傳的文件類型
            type: Array,
            default() {
                return [];
            }
        },
        acceptErrorMessage: {  //文件類型錯(cuò)誤的提示內(nèi)容
            type: String,
            default: '文件類型錯(cuò)誤'
        },
        compress: {         //是否開(kāi)啟圖片壓縮
            type: Boolean,
            default: true,
        },
        compressSize: {      //超過(guò)大小的圖片壓縮
            type: Number,
            default: 512,
        },
        data: {              //上傳附帶的內(nèi)容
            type: Object,
            default() {
                return {};
            },
        },
        action: {           //上傳地址
            type: String,
            default: '',
        },
        headers: {         //設(shè)置上傳的請(qǐng)求頭部
            type: Object,
            default() {
                return {};
            },
        },
        imgWidth: {    //圖片壓縮時(shí)指定壓縮的圖片寬度
            type: [Number, Boolean],
            default: 800,
        },
        quality: {    //圖片壓縮的質(zhì)量
            type: Number,
            default: 1,
        },
        beforeUpload: {  //上傳文件之前的鉤子
            type: Function
        },
        onSuccess: {     //上傳成功的鉤子
            type: Function
        },
        onError: {       //上傳失敗的鉤子
            type: Function
        },
        onLoadend: {     //文件上傳成功或者失敗都會(huì)執(zhí)行的鉤子
            type: Function
        },
        onProgress: {    //文件上傳進(jìn)度的鉤子
            type: Function
        },
        onSuccessText: {   //上傳成功的提示內(nèi)容
            type: String,
            default: '上傳成功'
        },
        onErrorText: {    //上傳失敗的提示內(nèi)容
            type: String,
            default: '上傳失敗'
        },
        beforeRemove: {    //刪除文件的鉤子
            type: Function
        },
        showRemove: {     //是否展示刪除icon
            type: Boolean,
            default: true
        },
        type: {           //單文件上傳還是多文件上傳
            type: String,
            default: 'single',
            validator: function (value) {
                return ["single", "multiple"].includes(value);
            }
        },
        maxNumber: {    //多文件上傳最多上傳的個(gè)數(shù)
            type: Number
        },
        isImage: {      //文件是否為圖片
            type: Boolean,
            default: true
        }
    }
}

2. 上傳組件的實(shí)現(xiàn)

template

<template>
  <div class="g7-Upload-single">
    <!-- 占位內(nèi)容,圖片展示,文件展示的處理 -->
    <div class="g7-Upload-default-icon">
      <template v-if="!value">
        <slot>
          <Icon :size="iconSize" :icon="icon" />
        </slot>
      </template>
      <template v-else>
        <template v-if="isImage">
          <img class="g7-Upload-img" :src="value" />
        </template>
        <template v-else>
          <Icon :size="34" icon="iconicon-" />
        </template>
        <span @click.stop="onRemove" v-if="showRemove" class="g7-Upload-removeImg">
          <Icon :size="14" icon="iconcuowu" color="#fff" />
        </span>
      </template>
    </div>
    <input
      class="g7-Upload-input"
      @change="change"
      :disabled="computedDisabled"
      :name="name"
      type="file"
      ref="input"
    />
    <!-- 圖片壓縮需要用到的canvas -->
    <canvas hidden="hidden" v-if="compress" ref="canvas"></canvas>
    <!-- 進(jìn)度條 -->
    <div v-if="progress > 0" class="g7-Upload-progress">
      <div :style="{width:`${progress}%`}" class="g7-Upload-progress-bar"></div>
    </div>
  </div>
</template>

文件壓縮實(shí)現(xiàn):

canvasDataURL(base) {
      const img = new Image();
      img.src = base;
      const that = this;
      function ImgOnload() {
        /**
         * 計(jì)算生成圖片的寬高
         */
        const scale = this.width / this.height;
        const width =
          that.imgWidth === false || this.width <= that.imgWidth
            ? this.width
            : that.imgWidth;
        const height = width / scale;
        const canvas = that.$refs.canvas;
        canvas.width = width;
        canvas.height = height;
        //利用canvas繪制壓縮的圖片并生成新的圖片
        const context = canvas.getContext("2d");
        context.drawImage(this, 0, 0, width, height);
        canvas.toBlob(
          blob => {
            that.file = blob;
            that.upload(blob);
            that.$emit("on-change", blob, that.options);
          },
          "image/png",
          that.quality
        );
        /**
         * 使用完的createObjectURL需要釋放內(nèi)存
         */
        window.URL.revokeObjectURL(this.src);
      }
      img.onload = ImgOnload;
    }

上傳文件的實(shí)現(xiàn):

export function fetch(options, file) {
  if (typeof XMLHttpRequest === 'undefined') {
    return;
  }
  const xhr = new XMLHttpRequest();
  const action = options.action;
  if (xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
      if (e.total > 0) {
        e.percent = e.loaded / e.total * 100;
      }
      options.uploadProgress(e);
    };
  }
  const formData = new FormData();
  formData.append(options.name, file, options.fileName);
  for (const key in options.data) {
    formData.append(key, options.data[key]);
  }
  // 成功回調(diào)
  xhr.onload = (e) => {
    const response = e.target.response;
    if (xhr.status < 200 || xhr.status >= 300) {
      options.uploadError(response);
      return;
    }
    options.onload(response);
  };
  // 出錯(cuò)回調(diào)
  xhr.onerror = (e) => {
    const response = e.target.response;
    options.uploadError(response);
  };
  // 請(qǐng)求結(jié)束
  xhr.onloadend = (e) => {
    const response = e.target.response;
    options.uploadLoadend(response);
  };
  xhr.open('post', action, true);
  const headers = options.headers;
  for (const key in headers) {
    if (headers[key] !== null) {
      xhr.setRequestHeader(key, headers[key]);
    }
  }
  xhr.send(formData);
}

3. 完整的代碼

上傳組件:

<!-- components/upload.vue -->
<template>
  <div class="g7-Upload-single">
    <div class="g7-Upload-default-icon">
      <template v-if="!value">
        <slot>
          <Icon :size="iconSize" :icon="icon" />
        </slot>
      </template>
      <template v-else>
        <template v-if="isImage">
          <img class="g7-Upload-img" :src="value" />
        </template>
        <template v-else>
          <Icon :size="34" icon="iconicon-" />
        </template>
        <span @click.stop="onRemove" v-if="showRemove" class="g7-Upload-removeImg">
          <Icon :size="14" icon="iconcuowu" color="#fff" />
        </span>
      </template>
    </div>
    <input
      class="g7-Upload-input"
      @change="change"
      :disabled="computedDisabled"
      :name="name"
      type="file"
      ref="input"
    />
    <!-- 圖片壓縮需要用到的canvas -->
    <canvas hidden="hidden" v-if="compress" ref="canvas"></canvas>
    <!-- 進(jìn)度條 -->
    <div v-if="progress > 0" class="g7-Upload-progress">
      <div :style="{width:`${progress}%`}" class="g7-Upload-progress-bar"></div>
    </div>
  </div>
</template>

<script>
import Icon from "../../Icon";     //自定義組件
import mixins from "./mixins";
import { getType, fetch } from "./utils";
import Toast from "../../~Toast";      //自定義組件
const compressList = ["png", "PNG", "jpg", "JPG", "jpeg", "JPEG"];
export default {
  components: { Icon },
  mixins: [mixins],
  props: {
    value: {
      type: String
    }
  },
  data() {
    return {
      file: "",
      progress: 0,
      src: ""
    };
  },
  computed: {
    computedDisabled() {
      return this.disabled || this.progress !== 0;
    }
  },
  methods: {
    change(e) {
      if (this.disabled) {
        return;
      }
      const file = e.target.files[0];
      if (!file) {
        return;
      }

      const type = getType(file.name);
      if (this.accept.length) {
        if (!this.accept.includes(type)) {
          Toast.info(this.acceptErrorMessage);
          return;
        }
      }

      const size = Math.round((file.size / 1024) * 100) / 100;
      if (size > this.size) {
        Toast.info(`請(qǐng)上傳小于${this.size / 1024}M的文件`);
        return;
      }
      if (this.isCompress(type, size)) {
        this.canvasDataURL(URL.createObjectURL(file));
        return;
      }
      this.$emit("on-change");
      this.file = file;
      this.upload(file);
    },
    /**
     * 判斷是否滿足壓縮條件
     */
    isCompress(type, size) {
      return (
        this.compress && compressList.includes(type) && size > this.compressSize
      );
    },
    canvasDataURL(base) {
      const img = new Image();
      img.src = base;
      const that = this;
      function ImgOnload() {
        /**
         * 計(jì)算生成圖片的寬高
         */
        const scale = this.width / this.height;
        const width =
          that.imgWidth === false || this.width <= that.imgWidth
            ? this.width
            : that.imgWidth;
        const height = width / scale;
        const canvas = that.$refs.canvas;
        canvas.width = width;
        canvas.height = height;
        //利用canvas繪制壓縮的圖片并生成新的blob
        const context = canvas.getContext("2d");
        context.drawImage(this, 0, 0, width, height);
        canvas.toBlob(
          blob => {
            that.file = blob;
            that.upload(blob);
            that.$emit("on-change", blob, that.options);
          },
          "image/png",
          that.quality
        );
        /**
         * 使用完的createObjectURL需要釋放內(nèi)存
         */
        window.URL.revokeObjectURL(this.src);
      }
      img.onload = ImgOnload;
    },
    /**
     * 上傳成功
     */
    onload(e) {
      this.progress = 0;
      this.$emit("input", e);
      if (this.onSuccess) {
        this.onSuccess(this.file, e);
        return;
      }
      Toast.info(this.onSuccessText);
    },
    /**
     * 上傳進(jìn)度
     */
    uploadProgress(e) {
      this.progress = e.percent;
      if (this.onProgress) {
        this.onProgress(this.file, e);
      }
    },
    /**
     * 上傳失敗
     */
    uploadError(e) {
      this.progress = 0;
      if (this.onError) {
        this.onSuccess(this.file, e);
        return;
      }
      Toast.info(this.onErrorText);
    },
    /**
     * 請(qǐng)求結(jié)束
     */
    uploadLoadend(e) {
      this.clearInput();
      if (this.onloadend) {
        this.onloadend(this.file, e);
      }
    },
    /**
     * 上傳
     */
    upload(file) {
      this.clearInput();
      if (!this.beforeUpload) {
        fetch(this, file);
        return;
      }

      const before = this.beforeUpload(file);
      if (before && before.then) {
        before.then(res => {
          if (res !== false) {
            fetch(this, file);
          }
        });
        return;
      }
      if (before !== false) {
        fetch(this, file);
      }
    },
    /**
     * 刪除文件
     */
    onRemove() {
      this.clearInput();
      if (this.type === "single") {
        if (!this.beforeRemove) {
          this.$emit("input", "");
          return;
        }

        const before = this.beforeRemove(this.file, this.value);
        if (before && before.then) {
          before.then(res => {
            if (res !== false) {
              this.$emit("input", "");
            }
          });
          return;
        }
        if (before !== false) {
          this.$emit("input", "");
        }
        return;
      }
      this.$emit("on-remove");
    },
    clearInput() {
      this.$refs.input.value = null;
    }
  }
};
</script>

utils.js:獲取文件的后綴

/**
 * 獲取文件的后綴
 * @param {*} file
 */
export const getType = file => file.substr(file.lastIndexOf('.') + 1);

/**
 * 請(qǐng)求封裝
 * @param {*} options
 * @param {*} file
 */
export function fetch(options, file) {
  if (typeof XMLHttpRequest === 'undefined') {
    return;
  }
  const xhr = new XMLHttpRequest();
  const action = options.action;
  if (xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
      if (e.total > 0) {
        e.percent = e.loaded / e.total * 100;
      }
      options.uploadProgress(e);
    };
  }
  const formData = new FormData();
  formData.append(options.name, file, options.fileName);
  for (const key in options.data) {
    formData.append(key, options.data[key]);
  }
  // 成功回調(diào)
  xhr.onload = (e) => {
    const response = e.target.response;
    if (xhr.status < 200 || xhr.status >= 300) {
      options.uploadError(response);
      return;
    }
    options.onload(response);
  };
  // 出錯(cuò)回調(diào)
  xhr.onerror = (e) => {
    const response = e.target.response;
    options.uploadError(response);
  };
  // 請(qǐng)求結(jié)束
  xhr.onloadend = (e) => {
    const response = e.target.response;
    options.uploadLoadend(response);
  };
  xhr.open('post', action, true);
  const headers = options.headers;
  for (const key in headers) {
    if (headers[key] !== null) {
      xhr.setRequestHeader(key, headers[key]);
    }
  }
  xhr.send(formData);
}

整個(gè)Upload組件的對(duì)外暴露組件:

<template>
  <div class="g7-Upload">
    <template v-if="type === 'single'">
      <Upload
        :icon="icon"
        :size="size"
        :accept="accept"
        :name="name"
        :acceptErrorMessage="acceptErrorMessage"
        :compress="compress"
        :iconSize="iconSize"
        :compressSize="compressSize"
        :imgWidth="imgWidth"
        :response="response"
        :showLabel="showLabel"
        :headers="headers"
        :action="action"
        :data="data"
        :quality="quality"
        :beforeRemove="beforeRemove"
        :beforeUpload="beforeUpload"
        :onSuccess="onSuccess"
        :onSuccessText="onSuccessText"
        :onError="onError"
        :onProgress="onProgress"
        :onLoadend="onLoadend"
        :onErrorText="onErrorText"
        :disabled="disabled"
        :showRemove="showRemove"
        v-model="currentValue"
        @input="input"
        :type="type"
        :maxNumber="maxNumber"
        :isImage="isImage"
      >
        <slot></slot>
      </Upload>
    </template>
  </div>
</template>

<script>
import Upload from "./components/upload";
import mixins from "./components/mixins";
export default {
  name: "G-Upload",
  components: { Upload },
  mixins: [mixins],
  props: {
    value: {
      type: [String, Array]
    }
  },
  data() {
    return {
      currentValue: this.value
    };
  },
  watch: {
    value(val) {
      this.currentValue = val;
    }
  },
  methods: {
    input(val) {
      this.$emit("input", val);
    },
    //接管文件上傳時(shí),自定義文件上傳進(jìn)度
    onProgress(percent) {
      this.$refs.upload.uploadProgress(percent);
    }
  }
};
</script>

因?yàn)閳D片和文件展示的占位圖不一樣,所以用一個(gè)isImage的參數(shù)來(lái)判斷傳入的文件是否為圖片的方式總感覺(jué)很傻。但是當(dāng)網(wǎng)絡(luò)資源的url沒(méi)有文件后綴時(shí)沒(méi)法分辨出來(lái)是圖片還是文件,各位大佬有木有好的解決方式。

效果圖

圖片壓縮前后大小對(duì)比

到此這篇關(guān)于實(shí)現(xiàn)一個(gè)Vue版Upload組件的文章就介紹到這了,更多相關(guān)Vue Upload組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IntelliJ IDEA編輯器配置vue高亮顯示

    IntelliJ IDEA編輯器配置vue高亮顯示

    這篇文章主要為大家詳細(xì)介紹了IntelliJ IDEA編輯器配置vue高亮顯示,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • 將VUE項(xiàng)目部署到服務(wù)器的詳細(xì)步驟

    將VUE項(xiàng)目部署到服務(wù)器的詳細(xì)步驟

    經(jīng)過(guò)一段時(shí)間的探索,前端后端都有大致的樣子了,下面就是部署到服務(wù)器,這篇文章主要給大家介紹了關(guān)于將VUE項(xiàng)目部署到服務(wù)器的詳細(xì)步驟,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • vue checkbox 全選 數(shù)據(jù)的綁定及獲取和計(jì)算方法

    vue checkbox 全選 數(shù)據(jù)的綁定及獲取和計(jì)算方法

    下面小編就為大家分享一篇vue checkbox 全選 數(shù)據(jù)的綁定及獲取和計(jì)算方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • vue如何實(shí)現(xiàn)簡(jiǎn)易的彈出框

    vue如何實(shí)現(xiàn)簡(jiǎn)易的彈出框

    這篇文章主要介紹了vue如何實(shí)現(xiàn)簡(jiǎn)易的彈出框,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue3?hook自動(dòng)導(dǎo)入原理解析

    vue3?hook自動(dòng)導(dǎo)入原理解析

    這篇文章主要介紹了vue3?hook自動(dòng)導(dǎo)入的原理,介紹了API的自動(dòng)導(dǎo)入及組件的自動(dòng)導(dǎo)入,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • 如何在vue中使用百度地圖添加自定義覆蓋物(水波紋)

    如何在vue中使用百度地圖添加自定義覆蓋物(水波紋)

    這篇文章主要給大家介紹了關(guān)于如何在vue中使用百度地圖添加自定義覆蓋物(水波紋)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • vue開(kāi)發(fā)之LogicFlow自定義業(yè)務(wù)節(jié)點(diǎn)

    vue開(kāi)發(fā)之LogicFlow自定義業(yè)務(wù)節(jié)點(diǎn)

    這篇文章主要為大家介紹了vue開(kāi)發(fā)之LogicFlow自定義業(yè)務(wù)節(jié)點(diǎn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • vue請(qǐng)求按順序執(zhí)行的示例詳解

    vue請(qǐng)求按順序執(zhí)行的示例詳解

    我們有時(shí)候會(huì)碰到這種情況,需要連續(xù)發(fā)送兩個(gè)請(qǐng)求,第二個(gè)請(qǐng)求需要用第一個(gè)請(qǐng)求的某個(gè)返回值作為參數(shù)來(lái)作為第二個(gè)請(qǐng)求的請(qǐng)求參數(shù),這篇文章主要介紹了vue請(qǐng)求如何按順序執(zhí)行,需要的朋友可以參考下
    2023-12-12
  • 詳解基于vue的移動(dòng)web app頁(yè)面緩存解決方案

    詳解基于vue的移動(dòng)web app頁(yè)面緩存解決方案

    這篇文章主要介紹了詳解基于vue的移動(dòng)web app頁(yè)面緩存解決方案,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-08-08
  • 關(guān)于Vue源碼vm.$watch()內(nèi)部原理詳解

    關(guān)于Vue源碼vm.$watch()內(nèi)部原理詳解

    這篇文章主要介紹了關(guān)于Vue源碼vm.$watch()內(nèi)部原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評(píng)論