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

vuecli+AXdownload下載組件封裝?+css3下載懸浮球動畫效果

 更新時間:2024年05月24日 15:54:02   作者:阿里爸爸首席執(zhí)行官  
當觸發(fā)下載功能的時候,會觸發(fā)一個下載動畫,下載懸浮球會自動彈出,并且閃爍提示有新的下載任務直到下載任務完場提示,接下來通過本文介紹vuecli+AXdownload下載組件封裝?+css3下載懸浮球動畫效果,需要的朋友可以參考下

在之前我們寫過一個上傳組件,現(xiàn)在我們在寫一個下載組件,下面可能依賴了一些公用的工具類 有任何的使用疑問可以私信!??!

一、效果展示

1. 下載觸發(fā)效果

當觸發(fā)下載功能的時候,會觸發(fā)一個下載動畫,下載懸浮球會自動彈出,并且閃爍提示有新的下載任務直到下載任務完場提示

在提示有新下載之后,懸浮球會半隱在屏幕右下角,如果所有文件的下載進度均完成,懸浮球會在1分鐘后小消失

2. 下載中的進度,詳情展示

點擊懸浮球可以看到下載的文件列表,并對文件的下載進度,速度等信息進行展示

3.下載失敗

當文件下載過程中,由于各種原因導致文件失敗的時候,懸浮球會發(fā)生閃爍,并提示下載失敗

二、優(yōu)點

1.本組件將使用vuex對下載數(shù)據(jù)文件在session中做持久化,以防止數(shù)據(jù)在刷新頁面后丟失。
2.支持多文件下載展示
3.擁有假進度條和真進度條兩種,可根據(jù)業(yè)務需求,自行選擇
4.對大型文件的下載給予用戶動畫提示,增強用戶體驗,以防服務器響應過長,導致頁面沒有響應,用戶會覺得系統(tǒng)功能沒有被觸發(fā)。
5.支持私有npm包引用

三、代碼展示

因為我這邊已經封裝到了npm包中,所以有些代碼可能需要根據(jù)自己的項目進行自行調整(有任何的使用疑問可以私信?。?!)
代碼將分為兩個部分
1. 工具類
2. ui組件部分
3. 下載方法部分

UI組件部分

工具類 TableUtil.js

export const FileState = {
  // 等待上傳或者下載
  Waiting: 0,
  // 上傳中或者下載中
  uploadDownloadStatus: 1,
  // 上傳成功
  Success: 2,
  // 上傳失敗
  Error: 3,
  // 等待服務器處理
  WaitServer: 4,
};
export class TableUtils {
  static formatFileSize(fileSize) {
    if (fileSize < 1024) {
      return `${fileSize.toFixed(2)}B`;
    }
    if (fileSize < 1024 * 1024) {
      let temp = fileSize / 1024;
      temp = +temp.toFixed(2);
      return `${temp}KB`;
    }
    if (fileSize < 1024 * 1024 * 1024) {
      let temp = fileSize / (1024 * 1024);
      temp = +temp.toFixed(2);
      return `${temp}MB`;
    }
    let temp = fileSize / (1024 * 1024 * 1024);
    temp = +temp.toFixed(2);
    return `${temp}GB`;
  }
}
export function objectToFormData(obj) {
  const formData = new FormData();
  Object.keys(obj).forEach(key => {
    formData.append(key, obj[key]);
  });
  return formData;
}
export function getIconByFileName(file) {
  // 文件擴展名
  const parts = file.name.split(".");
  const ext = parts.length > 1 ? parts[parts.length - 1].toLowerCase() : "";
  // 文件擴展名和圖標的映射關系
  const mapping = {
    audio: "mp3,wav,aac,flac,ogg,wma,m4a",
    doc: "doc,docx",
    pdf: "pdf",
    ppt: "ppt,pptx",
    txt: "txt",
    video: "mp4,avi,wmv,rmvb,mkv,mov,flv,f4v,m4v,rm,3gp,dat,ts,mts,vob",
    xls: "xls,xlsx",
    zip: "zip,rar,7z",
    pic: "jpg,jpeg,png,gif,bmp,webp",
  };
  // 根據(jù)文件擴展名獲取對應的圖標
  let icon = "file";
  Object.keys(mapping).forEach(key => {
    const exts = mapping[key].split(",");
    if (exts.includes(ext)) {
      icon = key;
    }
  });
  return `icon-${icon}-m`;
}

ui組件部分 AXDownload.vue

主要是容納懸浮球和文件列表的主容器

<template>
  <div v-if="showBall">
    <!-- 類名不要改,防止沖突 -->
    <div
      id="ax-private-download-continer"
      :class="{
        'ax-private-download-continer-add-newtask': addNewTask,
      }"
      @click="showFloatBall()"
      @mouseleave="hideFloatBall"
      @mouseenter="enterBall"
    >
      <div
        class="ax-private-download-text-content"
        :class="{
          'ax-private-circle-add-active': TaskAnminate === '添加',
          'ax-private-circle-error-active': TaskAnminate === '失敗',
        }"
      >
        <div v-html="ballText"></div>
      </div>
      <DownloadFloatingBall :TaskAnminate="TaskAnminate"></DownloadFloatingBall>
    </div>
    <FileDownListDialog ref="fileDownListDialog"></FileDownListDialog>
  </div>
</template>
<script>
import DownloadFloatingBall from "./components/DownloadFloatingBall.vue";
import FileDownListDialog from "./components/FileDownListDialog.vue";
import { FileState } from "../../../src/utils/TableUtil";
export default {
  name: "AxDownLoad",
  components: {
    DownloadFloatingBall,
    FileDownListDialog,
  },
  data() {
    return {
      //顯示出 懸浮球
      showDownloadBall: false,
      timer: null, //計時自動移入
      //延遲移入移出
      moveTimer: null, //移出時間器
      addNewTask: false, //是否是添加的新任務
      newTaskTimer: null,
      showBall: false,
      TaskAnminateTimer: null,
      balloldText: "我的下載",
      ballText: "",
      TaskAnminate: "",
      hideDownloadBallTimer: null,
    };
  },
  mounted() {
    const downloadList = this.$store.state.file.downloadList;
    this.showBall = downloadList.length > 0;
    this.ballText = downloadList.length > 0 ? `下載任務${"<br />"}${downloadList.length}個` : this.balloldText;
  },
  methods: {
    hideFloatBall(event) {
      this.moveTimer = setTimeout(() => {
        if (this.timer) {
          clearInterval(this.timer);
        }
        document.getElementById("ax-private-download-continer").style.transform = "translateX(0px)";
        this.showDownloadBall = false;
      }, 500);
    },
    enterBall() {
      if (this.moveTimer) {
        clearTimeout(this.moveTimer);
      }
    },
    showFloatBall() {
      if (!this.showDownloadBall) {
        //顯示出 懸浮球
        this.showDownloadBall = true;
        document.getElementById("ax-private-download-continer").style.transform = "translateX(-100px)";
      } else {
        //點擊懸浮球,展示下載的附件列表
        this.$refs.fileDownListDialog.showDialog({}, 0);
      }
    },
    //添加新的下載任務 動畫
    addDownloadTask(text) {
      this.showDownloadBall = true;
      this.addNewTask = true;
      this.TaskAnminate = text;
      if (this.newTaskTimer) {
        clearInterval(this.newTaskTimer);
      }
      this.newTaskTimer = setTimeout(() => {
        this.addNewTask = false;
        this.TaskAnminate = "";
      }, 3000);
    },
    clearAnimateTask() {
      this.TaskAnminate = "";
      this.ballText = this.balloldText;
    },
    //延時動畫
    delayAnimate(func) {
      if (this.TaskAnminateTimer) {
        clearInterval(this.TaskAnminateTimer);
      }
      this.TaskAnminateTimer = setTimeout(() => {
        func();
      }, 500);
    },
    isAllEnd(downloadList) {
      // 判斷下載列表中每一個文件的狀態(tài)是否為:等待、上傳下載狀態(tài)、等待服務器
      const flag = downloadList.every(
        item =>
          item.state !== FileState.Waiting &&
          item.state !== FileState.uploadDownloadStatus &&
          item.state !== FileState.WaitServer
      );
      if (flag) {
        if (this.hideDownloadBallTimer) {
          clearInterval(this.hideDownloadBallTimer);
        }
        //下載全部完成,隱藏懸浮球
        this.ballText = `下載任務完成`;
        this.hideDownloadBallTimer = setTimeout(() => {
          this.showBall = false;
          this.$store.commit("CLEAR_DOWNLOAD_LIST");
        }, 60000);
      } else {
        if (this.hideDownloadBallTimer) {
          clearInterval(this.hideDownloadBallTimer);
        }
      }
    },
  },
  watch: {
    showDownloadBall(newVal, oldVal) {
      if (newVal) {
        this.timer = setTimeout(() => {
          this.hideFloatBall();
        }, 5000);
      }
    },
    "$store.state.file.downloadList": {
      handler(newVal, oldVal) {
        // 在這里處理變化
        this.showBall = newVal.length > 0;
        this.balloldText = `下載任務${"<br />"}${newVal.length}個`;
        this.ballText = this.balloldText;
        this.isAllEnd(newVal);
      },
      deep: true,
    },
    "$store.state.file.errorEvent": {
      handler(newVal, oldVal) {
        this.addDownloadTask("失敗");
        this.$message({
          type: "warning",
          message: `${newVal.name}下載失敗了!`,
        });
        this.ballText = "下載失敗!";
        this.delayAnimate(this.clearAnimateTask);
      },
      deep: true,
    },
    "$store.state.file.downloadEventCount": {
      handler(newVal, oldVal) {
        this.addDownloadTask("添加");
        this.$message({
          type: "success",
          message: "您添加了新的下載任務!",
        });
        this.ballText = "新下載!";
        this.delayAnimate(this.clearAnimateTask);
      },
      deep: true,
    },
  },
};
</script>
<style lang="scss" scoped>
#ax-private-download-continer {
  position: fixed;
  transition: transform 0.3s ease; /* 持續(xù)時間和緩動函數(shù)可以調整 */
  transform: translateX(0px); /* 初始轉換狀態(tài) */
  right: -50px;
  bottom: 100px;
  width: 100px;
  height: 100px;
  z-index: 99999;
  border-radius: 100%;
  text-align: center;
  line-height: 100px;
  -webkit-user-select: none; /* Safari */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* 非前綴版本,適用于Chrome和Opera */
  cursor: pointer;
  .ax-private-download-text-content {
    position: relative;
    color: #409eff;
    width: 90px;
    z-index: 2; /* 高于背景層 */
    line-height: 21px;
    font-weight: 600;
    top: 50%;
    right: 50%;
    transform: translate(50px, -44%);
  }
}
.ax-private-download-continer-add-newtask {
  transform: translateX(-100px) !important; /* 初始轉換狀態(tài) */
}
.ax-private-circle-add-active {
  animation: addTask 1s !important;
}
.ax-private-circle-error-active {
  animation: errorTask 1s !important;
}
@keyframes addTask {
  10% {
    color: #67c23a;
  }
  80% {
    color: #c9f6b2;
  }
}
@keyframes errorTask {
  10% {
    color: white;
  }
  80% {
    color: white;
  }
}
</style>

ui組件下載懸浮球 DownloadFloatingBall.vue

下載懸浮球的主體,以及懸浮球的動畫

<template>
  <!-- 類名不要改,防止沖突 -->
  <div
    class="ax-private-download-circle-container"
    :class="{
      'ax-private-download-circle-container-add-active': TaskAnminate == '添加',
      'ax-private-download-circle-container-error-active': TaskAnminate == '失敗',
    }"
  >
    <div
      v-for="(item, index) in 4"
      :key="index"
      class="ax-private-circle"
      :class="{
        'ax-private-circle-active': TaskAnminate !== '',
      }"
    ></div>
  </div>
</template>
<script>
export default {
  name: "DownloadFloatingBall",
  props: {
    TaskAnminate: {
      type: String,
      default: "",
    },
  },
  data() {
    return {};
  },
};
</script>
<style scoped>
.ax-private-download-circle-container {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  width: 100px;
  height: 100px;
  border-radius: 50%;
}
.ax-private-download-circle-container-add-active {
  animation: addTaskcontainer 1s !important;
}
.ax-private-download-circle-container-error-active {
  animation: errorTaskcontainer 1s !important;
}
@keyframes addTaskcontainer {
  10% {
    background-color: #2887e6;
  }
  100% {
    background-color: transparent;
  }
}
@keyframes errorTaskcontainer {
  10% {
    background-color: #f56c6c;
  }
  100% {
    background-color: transparent;
  }
}
.ax-private-download-circle-container .ax-private-circle {
  position: absolute;
  margin: auto;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  border-radius: 50%;
  background: rgba(204, 180, 225, 0.02);
  backdrop-filter: blur(5px); /* 應用模糊效果 */
}
.ax-private-circle-active {
  animation: addTask 1.5s !important;
}
.ax-private-circle-error-active {
  animation: errorTask 1.5s !important;
}
.ax-private-download-circle-container .ax-private-circle:nth-of-type(1) {
  width: 100px;
  height: 90px;
  animation: rt 6s infinite linear;
  box-shadow: 0 0 1px 0 #2887e6, inset 0 0 10px 0 #2887e6;
}
.ax-private-download-circle-container .ax-private-circle:nth-of-type(2) {
  width: 90px;
  height: 100px;
  animation: rt 10s infinite linear;
  box-shadow: 0 0 1px 0 #006edb, inset 0 0 10px 0 #006edb;
}
.ax-private-download-circle-container .ax-private-circle:nth-of-type(3) {
  width: 105px;
  height: 95px;
  animation: rt 5s infinite linear;
  /* box-shadow: 0 0 1px 0 #003c9b, inset 0 0 10px 0 #003c9b; */
  box-shadow: 0 0 1px 0 #0148ba, inset 0 0 10px 0 #0148ba;
}
.ax-private-download-circle-container .ax-private-circle:nth-of-type(4) {
  width: 95px;
  height: 105px;
  animation: rt 15s infinite linear;
  box-shadow: 0 0 1px 0 #01acfc, inset 0 0 10px 0 #01acfc;
}
@keyframes rt {
  100% {
    transform: rotate(360deg);
  }
}
@keyframes addTask {
  10% {
    transform: scale(1.5);
  }
  30% {
    transform: scale(0.6);
  }
  60% {
    transform: scale(1);
  }
}
</style>

ui組件下載文件列表彈窗 FileDownListDialog

主要是點擊懸浮球之后的彈窗,用于展示文件的列表

<template>
  <!-- 對話框 -->
  <el-dialog
    v-if="dialog.visible"
    ref="dialog"
    :title="getHeaderText"
    :visible.sync="dialog.visible"
    width="70%"
    :close-on-click-modal="false"
  >
    <div class="ax-private-file-container">
      <template v-if="fileTaskList.length > 0">
        <div class="ax-private-file-item" v-for="(item, index) in fileTaskList" :key="index">
          <div class="ax-file-progress" :style="{ width: `${item.process}%` }"></div>
          <div class="ax-file-content">
            <div class="ax-file-type-icon">
              <SvgIcon :icon-class="getIconByFileName({ name: item.name })"></SvgIcon>
            </div>
            <div class="ax-file-info">
              <div class="ax-file-filename">{{ item.name }}</div>
              <div class="ax-file-loadinfo">
                <span class="info-span">已下載:{{ item.loaded }}</span>
                <span class="info-span" v-if="item.size !== 'NaNGB'">文件大?。簕{ item.size }}</span>
                {{ getuploadStatus(item.state, item.message) }}
                <span
                  style="color: #409eff; cursor: pointer"
                  v-if="item.message && item.state == 3"
                  @click="showError(item.message)"
                >
                  查看詳情</span
                >
                {{ getSpeed(item) }}
              </div>
            </div>
            <div class="ax-file-operate">
              <i v-if="item.state == 0" class="el-icon-download" style="color: #909399"></i>
              <!-- 上傳中 -->
              <span v-else-if="item.state == 1 || item.state == 4"> {{ item.process }}%</span>
              <!-- 已完成 -->
              <i v-else-if="item.state == 2" class="el-icon-circle-check" style="color: #67c23a"></i>
              <i v-else-if="item.state == 3" class="el-icon-warning" style="color: #f56c6c"></i>
            </div>
          </div>
        </div>
      </template>
      <template v-else>
        <div class="ax-top-label">暫無下載文件記錄</div>
      </template>
    </div>
    <el-row type="flex" justify="end"> </el-row>
  </el-dialog>
</template>
<script>
import { getIconByFileName, FileState } from "../../../../src/utils/TableUtil.js";
const STATUS = {
  CREATE: 0,
  UPDATE: 1,
};
export default {
  name: "FileDownListDialog",
  props: {
    // 對話框標題
    textMap: {
      type: Object,
      default: () => ({
        add: "文件下載列表",
        edit: "編輯",
      }),
    },
  },
  data() {
    return {
      fileTaskList: [],
      // 對話框
      dialog: {
        // 對話框狀態(tài)
        status: null,
        // 對話框參數(shù),用于編輯時暫存id
        params: {},
        // 對話框是否顯示
        visible: false,
      },
      errorCount: 0,
      waitingOrUploadingCount: 0,
    };
  },
  computed: {
    // 對話框標題
    dialogTitle() {
      return this.dialog.status === STATUS.CREATE ? this.textMap.add : this.textMap.edit;
    },
    getHeaderText() {
      if (this.waitingOrUploadingCount > 0 || this.errorCount > 0) {
        if (this.waitingOrUploadingCount > 0) {
          return `正在下載,剩余
           ${this.waitingOrUploadingCount}
         個文件,其中(有${this.errorCount}個失敗)`;
        }
        return `下載任務完成,有
         ${this.errorCount}個失敗`;
      }
      return "所有下載任務完成";
    },
  },
  methods: {
    /**
     * 顯示對話框,父元素調用
     *
     * @param {Object} param 對話框保存時的參數(shù)
     * @param {Number} status 對話框狀態(tài)[添加:0,編輯:1],必須是STATUS枚舉
     * @param {Object} formValues 編輯時傳入所有字段的默認值
     */
    async showDialog(param = {}, status = STATUS.CREATE) {
      // 保存參數(shù)用于save方法
      this.dialog.params = param;
      this.dialog.status = status;
      this.fileTaskList = this.$store.state.file.downloadList;
      this.getFileStatus();
      this.dialog.visible = true;
    },
    getIconByFileName(item) {
      const file = {
        name: item.name,
      };
      return getIconByFileName(file);
    },
    // 取消按鈕點擊
    btnCancelOnClick() {
      this.dialog.visible = false;
      this.$emit("cancel");
    },
    showError(message) {
      this.$message.error(message);
    },
    getuploadStatus(state, message) {
      const mapping = ["等待下載,請稍后...", "下載中", "下載成功", "下載失敗", "等待服務器處理"];
      if (message) {
        return message.slice(0, 15);
      }
      return mapping[state];
    },
    getSpeed(item) {
      if (item.state === 2 || item.state === 3 || item.state === 4) {
        return "";
      }
      return item.state === 1 && item.speed === "速度計算中..." ? "" : item.speed;
    },
    getFileStatus() {
      // 計算state等于FileState.Waiting或FileState.Uploading的元素數(shù)量
      this.waitingOrUploadingCount = this.fileTaskList.filter(
        item =>
          item.state === FileState.WaitServer ||
          item.state === FileState.Waiting ||
          item.state === FileState.uploadDownloadStatus
      ).length;
      // 計算state等于FileState.Error的元素數(shù)量
      this.errorCount = this.fileTaskList.filter(item => item.state === FileState.Error).length;
    },
  },
  watch: {
    "$store.state.file.downloadList": {
      handler(newVal, oldVal) {
        // 在這里處理變化
        this.fileTaskList = newVal;
        this.getFileStatus();
      },
      deep: true,
    },
  },
};
</script>
<style lang="scss" scoped>
::v-deep .el-dialog__body {
  height: 680px;
}
.ax-private-file-container {
  width: 100%;
  height: 600px;
  overflow: auto;
  .ax-private-file-item {
    float: left;
    width: 100%;
    height: 100px;
    position: relative;
    .ax-file-progress {
      height: 100px;
      background-color: #f5f9ff;
      position: absolute;
      z-index: 0;
      left: 0px;
    }
    .ax-file-content {
      z-index: 9999;
      width: 100%;
      position: absolute;
      height: 100px;
      display: flex;
      align-items: center;
      border-bottom: 1px solid #e0e2e6;
    }
    .ax-file-type-icon {
      width: 70px;
      height: 70px;
      float: left;
      .SvgIcon {
        width: 100%;
        height: 100%;
      }
    }
    .ax-file-info {
      width: calc(100% - 170px);
      float: left;
      //   background-color: red;
      .ax-file-filename {
        width: 100%;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        font-size: 16px;
        font-weight: 600;
        color: black;
        margin-bottom: 5px;
      }
      .ax-file-loadinfo {
        width: 100%;
        font-weight: 400;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        color: #8e8e8e;
        .info-span {
          margin-right: 10px;
        }
      }
    }
    .ax-file-operate {
      width: 100px;
      height: 100px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 20px;
      float: right;
    }
  }
}
</style>

下載工具方法 download.ts

主要觸發(fā)ui動畫,觸發(fā)下載的方法

import Vue from "vue";
import { MessageBox } from "element-ui"; // eslint-disable-line
import guid from "./generator";
import { FileState, TableUtils } from "./TableUtil.js";
// import store from "../store/index";
interface FileItem {
  name: string;
  state?: number;
  size: number | string; //文件大小轉義 類似10mb
  total?: number | string; //文件字節(jié)大小 114882037
  loaded?: number | string; //已下載大小
  process?: number;
  speed?: string;
  id: string; //唯一鍵
  realId?: string; //真實文件id
  startTime?: number;
  message?: string; //文件下載提示一些文字或者錯誤
}
interface FilePojo {
  name: string; //文件名稱
  id?: string; //文件id
  size?: string | number; //文件大小
  total?: string | number; //文件總大小
}
function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
//模擬隨機進度
function getRandomProcess(fileItem) {
  let percentCompleted = 0;
  const randomInt = getRandomInt(1, 2);
  const randomMaxPro = getRandomInt(94, 97);
  if (fileItem.process < randomMaxPro) {
    fileItem.process += randomInt;
    percentCompleted = fileItem.process;
  } else {
    //無操作
    percentCompleted = fileItem.process;
  }
  return percentCompleted;
}
//判斷total是否為未知
function isHasTotal(fileItem, loaded, total) {
  let percentCompleted = 0;
  //如果total為0
  if (total === 0) {
    //如果文件大小為0,就說明文件的大小屬于未知狀態(tài),需要模擬進度條
    percentCompleted = getRandomProcess(fileItem);
  } else {
    //如果文件大小不為0,就可以計算真實的下載進度
    const realProcess = Math.round((loaded * 100) / total);
    if (realProcess > 80) {
      percentCompleted = getRandomProcess(fileItem);
    } else {
      percentCompleted = realProcess;
    }
  }
  return percentCompleted;
}
//監(jiān)聽下載進度
function onDownloadProgress(progressEvent, file) {
  //獲取下載列表
  const downloadList = Vue.prototype.$store.getters.downloadList;
  //如果下載列表不為空,且下載列表長度大于0
  if (downloadList && downloadList.length > 0) {
    //在下載列表中查找id與文件id相同的文件
    const index = downloadList.findIndex(i => i.id === file.id);
    let percentCompleted = 0;
    percentCompleted = isHasTotal(
      downloadList[index],
      progressEvent.loaded,
      file.total === 0 ? progressEvent.total : file.total
    );
    //如果索引大于-1,說明文件在下載列表中
    if (index > -1) {
      const currentTime = new Date().getTime();
      const timeInterval = (currentTime - downloadList[index].startTime) / 1000;
      const speed = progressEvent.loaded / timeInterval;
      downloadList[index].speed = `${TableUtils.formatFileSize(speed)}/秒`;
      const randomMaxPro = getRandomInt(94, 97);
      //更新進度條
      downloadList[index].process = percentCompleted;
      downloadList[index].loaded = TableUtils.formatFileSize(progressEvent.loaded);
      //更新文件狀態(tài)
      downloadList[index].state = FileState.uploadDownloadStatus;
      if (percentCompleted >= randomMaxPro) {
        //說明已經進入了模擬進度
        downloadList[index].state = FileState.WaitServer;
      }
      const fileItem = downloadList[index];
      Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
    }
  }
}
//獲取下載文件存進session
function setFileSessionStorage(file) {
  const newFile: FileItem = {
    name: file.name,
    state: FileState.Waiting,
    size: file.size || "未知",
    total: file.total || "未知",
    loaded: 0 || "未知", //已下載大小
    process: 0,
    speed: "速度計算中...",
    id: file.id,
    realId: file.realId,
    message: file.message || "",
    startTime: new Date().getTime(),
  };
  //判斷是否已經存在
  const downloadList = Vue.prototype.$store.getters.downloadList;
  // 如果下載列表存在且長度大于0
  if (downloadList && downloadList.length > 0) {
    // 查找下載列表中是否有與文件id相同的文件
    const index = downloadList.findIndex(i => i.id === file.id);
    // 如果沒有找到
    if (index === -1) {
      // 將文件添加到下載列表中
      Vue.prototype.$store.commit("ADD_DOWNLOAD_ITEM", newFile);
    } else {
      // 如果找到,更新下載列表中的文件
      Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: newFile, index: index });
    }
  } else {
    // 如果下載列表不存在或長度等于0,將文件添加到下載列表中
    Vue.prototype.$store.commit("SET_DOWNLOAD_LIST", [newFile]);
  }
  Vue.prototype.$store.commit("ADD_DOWNLOAD_EVENT_COUNT");
}
//判斷是get還是post
function isMethod(file, url, method, data, params) {
  return Vue.prototype.axios({
    url: url,
    method: method,
    responseType: "blob", // 確保以blob形式接收文件數(shù)據(jù)
    data: data,
    params: params, // 將查詢參數(shù)添加到請求中
    onDownloadProgress: progressEvent => {
      onDownloadProgress(progressEvent, file);
    },
  });
}
function setFileName(name) {
  const date = new Date();
  let fileName;
  if (/^.*\..{1,4}$/.test(name)) {
    fileName = name;
  } else {
    fileName = `${name} ${date.getFullYear()}年${date.getMonth() + 1}月
      ${date.getDate()}日${date.getHours()}時${date.getMinutes()}分${date.getSeconds()}秒.xls`;
  }
  return fileName;
}
/**
 * 通用下載 老版本
 *
 * @export
 * @param {String} url 請求地址
 * @param {String} name 文件名
 * @param {Object} params 請求參數(shù)
 * @param {String} requestType 請求方式(get,post)
 * @param {function} callBackFun 回調函數(shù)
 */
// eslint-disable-next-line
export  function download(url, name, data, requestType = 'get', params, callBackFun: Function = () => { },file?:FilePojo) {
  let axiosObj;
  const fileName = setFileName(name);
  let fileObj: FileItem = {
    name: fileName,
    id: guid(),
    size: "未知",
    realId: "",
    total: 0,
  };
  if (file) {
    fileObj = {
      name: file.name || fileName,
      id: guid(),
      realId: file.id || "",
      size: TableUtils.formatFileSize(Number(file.size)) || "未知",
      total: Number(file.size) || 0,
    };
  }
  //將即將要下載的文件存進session中
  setFileSessionStorage(fileObj);
  if (requestType === "get") {
    axiosObj = isMethod(fileObj, url, "get", {}, params);
  } else {
    //    axios.post(url, data, { responseType: "blob", params });
    axiosObj = isMethod(fileObj, url, "post", data, params);
  }
  axiosObj
    .then(res => {
      //獲取下載列表
      const downloadList = Vue.prototype.$store.getters.downloadList;
      const index = downloadList.findIndex(i => i.id === fileObj.id);
      if (!res) {
        //返回數(shù)據(jù)異常,附件要求失敗
        if (index !== -1) {
          //更新文件狀態(tài)
          downloadList[index].state = FileState.Error;
          downloadList[index].message = res.message || res.data.message || "文件下載失敗";
          const fileItem = downloadList[index];
          Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
          Vue.prototype.$store.commit("ERROR_EVENT", fileItem.name);
        }
        return;
      }
      // 如果返回類型為json 代表導出失敗 此時讀取后端返回報錯信息
      if (res.type === "application/json") {
        const reader: any = new FileReader(); // 創(chuàng)建一個FileReader實例
        reader.readAsText(res, "utf-8"); // 讀取文件,結果用字符串形式表示
        reader.onload = () => {
          // 讀取完成后,**獲取reader.result**
          const { message } = JSON.parse(reader.result);
          downloadList[index].state = FileState.Error;
          downloadList[index].message = message || "文件下載失敗";
          const fileItem = downloadList[index];
          Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
          Vue.prototype.$store.commit("ERROR_EVENT", fileItem.name);
          // 請求出錯
          MessageBox.alert(`${message}`, "操作失敗", {
            confirmButtonText: "我知道了",
            type: "warning",
            showClose: true,
          });
        };
        if (callBackFun) callBackFun("error");
        return;
      }
      const blob = new Blob([res]);
      let fileName;
      const date = new Date();
      if (/^.*\..{1,4}$/.test(name)) {
        fileName = name;
      } else if (res.headers && res.headers.includes("fileName=")) {
        fileName = decodeURIComponent(res.headers.split("fileName=")[1]);
      } else if (res.headers && res.headers.includes(`fileName*=utf-8''`)) {
        fileName = decodeURIComponent(res.headers.split(`fileName*=utf-8''`)[1]);
      } else {
        fileName = `${name} ${date.getFullYear()}年${
          date.getMonth() + 1
        }月${date.getDate()}日${date.getHours()}時${date.getMinutes()}分${date.getSeconds()}秒.xls`;
      }
      downloadList[index].name = fileName;
      downloadList[index].state = FileState.Success;
      downloadList[index].process = 100;
      const fileItem = downloadList[index];
      Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
      const aTag = document.createElement("a");
      aTag.style.display = "none";
      aTag.download = fileName;
      aTag.href = URL.createObjectURL(blob);
      document.body.appendChild(aTag);
      aTag.click();
      URL.revokeObjectURL(aTag.href);
      document.body.removeChild(aTag);
      if (callBackFun) callBackFun();
    })
    .catch(error => {
      // 處理錯誤
      const downloadList = Vue.prototype.$store.getters.downloadList;
      const index = downloadList.findIndex(i => i.id === fileObj.id);
      if (index !== -1) {
        //更新文件狀態(tài)
        downloadList[index].state = FileState.Error;
        const msg = JSON.stringify(error);
        downloadList[index].message = error.message || `文件下載失敗!${msg}`;
        const fileItem = downloadList[index];
        Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
        Vue.prototype.$store.commit("ERROR_EVENT", fileItem.name);
      }
    });
}
//新版本 推薦
export function downloadFile({ url, name, data, method, params, callBackFun, file }) {
  download(url, name, data, method, params, callBackFun, file);
}
//不走接口,虛假進度條
export function fakeDownProgress(file: FilePojo, func, funcArgs, message) {
  if (!file) {
    console.error("文件類型異常,file不能為null");
    return;
  }
  const fileObj = {
    name: file.name,
    id: guid(),
    realId: file.id || "",
    size: TableUtils.formatFileSize(Number(file.size)) || "未知",
    total: Number(file.size) || 0,
    message: message || "任務進行中",
  };
  setFileSessionStorage(fileObj);
  let timer;
  const downloadList = Vue.prototype.$store.getters.downloadList;
  const index = downloadList.findIndex(i => i.id === fileObj.id);
  if (index !== -1) {
    if (timer) {
      clearInterval(timer);
    }
    timer = setInterval(() => {
      downloadList[index].state = FileState.uploadDownloadStatus;
      const percentCompleted = isHasTotal(downloadList[index], 0, 0);
      downloadList[index].process = percentCompleted;
      const fileItem = downloadList[index];
      Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
    }, getRandomInt(800, 2000));
  }
  // eslint-disable-next-line no-async-promise-executor
  new Promise(async (resolve, reject) => {
    const res = await func(funcArgs);
    console.log(res);
    resolve(res);
  }).then(state => {
    console.log("state", state);
    if (timer) {
      clearInterval(timer);
    }
    console.log(index);
    if (index !== -1) {
      downloadList[index].state = state;
      if (downloadList[index].state === FileState.Success) {
        downloadList[index].process = 100;
        downloadList[index].message = "";
        const fileItem = downloadList[index];
        Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
      }
      if (downloadList[index].state === FileState.Error) {
        const fileItem = downloadList[index];
        Vue.prototype.$store.commit("UPDATE_DOWNLOAD_ITEM", { item: fileItem, index: index });
        Vue.prototype.$store.commit("ERROR_EVENT", fileItem.name);
      }
    }
  });
}

當我們注意到再download的方法中多次使用了store,所以我們要使用到vuex來做持久化

對應的store對象

const file = {
  state: {
    downloadList: [], //文件下載列表
    downloadEventCount: 0, //文件下載觸發(fā)次數(shù)
    errorEvent: {
      count: 0,
      name: "",
    }, //錯誤事件觸發(fā)
    successEvent: 0, //成功事件觸發(fā)
  },
  mutations: {
    SET_DOWNLOAD_LIST: (state, list) => {
      state.downloadList = list;
    },
    ADD_DOWNLOAD_EVENT_COUNT: state => {
      state.downloadEventCount += 1;
    },
    ADD_DOWNLOAD_ITEM: (state, item) => {
      state.downloadList = [...state.downloadList, item];
    },
    //修改downloadList其中的某個元素
    UPDATE_DOWNLOAD_ITEM: (state, { item, index }) => {
      state.downloadList.splice(index, 1, item);
    },
    //刪除downloadList所有元素
    CLEAR_DOWNLOAD_LIST: state => {
      state.downloadList = [];
    },
    CLEAR_ERROR_EVENT: state => {
      state.errorEvent.count = 0;
      state.errorEvent.name = "";
    },
    ERROR_EVENT: (state, name) => {
      state.errorEvent.count += 1;
      state.errorEvent.name = name;
    },
    SUCCESS_EVENT: state => {
      state.successEvent += 1;
    },
  },
  actions: {},
};
export default file;

持久化vuex store對象的入口處

import Vue from "vue";
import Vuex from "vuex";
import createPersistedState from "vuex-persistedstate";
import app from "./modules/app";
import user from "./modules/user";
import file from "./modules/file";
import getters from "./getters";
Vue.use(Vuex);
const store = new Vuex.Store({
  // 注意:新增的modules如果需要持久化還需要在plugins配置一下
  modules: {
    app,
    user,
    file,
  },
  getters,
  // 局部持久化,之所以不能全部持久化,詳見src/permission.js
  plugins: [
    createPersistedState({
      paths: ["app", "file"],
      storage: window.sessionStorage,
    }),
  ],
});
export default store;

getters中配置對應的屬性,用于獲取

const getters = {
  //文件管理
  downloadList: state => state.file.downloadList,
};
export default getters;

下載組件的使用

在使用download.ts中的方法觸發(fā)下載之前,需要引入ui組件,在App.vue中,引用

<template>
  <div id="app" v-loading.fullscreen.lock="$store.state.app.isLoading" element-loading-text="請稍候">
    <router-view />
    <AxDownLoad></AxDownLoad>
  </div>
</template>

在使用下載組件的時候會用到的一些內部方法

      import {download,downloadFile, fakeDownProgress, FileState } from 'download.ts';

使用例子 采用下列方法,可以明確傳遞的參數(shù)是什么,便于后續(xù)維護更新,復用

 btnDownloadOnClick(row) {
      const { fileName, fileExtension, pictureBase64Code, affixId } = row;
        const url = `${this.API_URL}iqpQuery/file/flowAffixDownload`;
        const params = { affixInfoId: affixId };
        //采用下列方法,可以明確傳遞的參數(shù)是什么,便于后續(xù)維護更新,復用
        downloadFile({
          url,
          name: `${fileName}.${fileExtension}`,
          params,
        });
    },

如果希望下載進度為真實進度,那么可以考慮上傳file這個對象,里面的size,把真實的文件大小傳入,或者由服務端在header加上contentLength

fakeDownProgress方法

此方法為虛假的進度展示,以便于一些沒有進度功能的長期方法的進度展示,
//使用fakeDownProgress方法進行進度展示,
//依次參數(shù)說明
//file:為FilePojo類型,可以傳遞文件id,也可以不傳遞,name必須傳遞
//func:需要等待的方法
//funcArgs:方法需要傳遞的對象,
//message:進度展示的文字信息
使用例子
//這是一個文件轉碼的方法,消耗時間的大小,不可計算,需要使用Promise方法進行包裹,除此以外,可以再執(zhí)行完成后的使用 resolve(FileState.Success);,失敗同理!

// A code block
var foo = 'bar';
 Base64FileEvent({ id, base64String, fileName, fileExtension }) {
      return new Promise((resolve, reject) => {
        const byteCharacters = atob(base64String);
        const byteNumbers = new Array(byteCharacters.length);
        // eslint-disable-next-line no-plusplus
        for (let i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], { type: 'application/octet-stream' });
        const downloadLink = document.createElement('a');
        const url = window.URL.createObjectURL(blob);
        console.log(url);
        downloadLink.href = url;
        downloadLink.download = `${fileName}.${fileExtension}`;
          downloadLink.click();
EventListener('click', () => {
          document.body.removeChild(downloadLink);
          window.URL.revokeObjectURL(url);
          resolve(FileState.Success);
        });
        // setTimeout(() => {
        //   resolve(2);
        // }, 2000);
      });
    },
     downloadBase64AsFile(id, base64String, fileName, fileExtension) {
      const data = {
        id,
        base64String,
        fileName,
        fileExtension,
      };
      const file = {
        id,
        name: `${fileName}.${fileExtension}`,
      };
      //使用fakeDownProgress方法進行進度展示,
      //依次參數(shù)說明
      //file:為FilePojo類型,可以傳遞文件id,也可以不傳遞,name必須傳遞
      //func:需要等待的方法
      //funcArgs:方法需要傳遞的對象,
      //message:進度展示的文字信息
      fakeDownProgress(file, this.Base64FileEvent, data, '文件轉碼中...');
    },

到此這篇關于vuecli+AXdownload下載組件封裝 +css3下載懸浮球動畫的文章就介紹到這了,更多相關vuecli+AXdownload下載組件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • vue 組件開發(fā)原理與實現(xiàn)方法詳解

    vue 組件開發(fā)原理與實現(xiàn)方法詳解

    這篇文章主要介紹了vue 組件開發(fā)原理與實現(xiàn)方法,結合實例形式詳細分析了vue.js組件開發(fā)的原理與實現(xiàn)方法,需要的朋友可以參考下
    2019-11-11
  • vue實現(xiàn)進入全屏和退出全屏的示例代碼

    vue實現(xiàn)進入全屏和退出全屏的示例代碼

    最近一個項目需要進行大屏展示,所以登錄完就要處于一個全屏的狀態(tài),本文主要介紹了vue實現(xiàn)進入全屏和退出全屏的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • 基于vue實現(xiàn)圓形菜單欄組件

    基于vue實現(xiàn)圓形菜單欄組件

    這篇文章主要介紹了基于vue實現(xiàn)的圓形菜單欄組件,本文通過實例代碼,圖文詳解的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-07-07
  • vue3 el-pagination 將組件中英文‘goto’ 修改 為 中文到‘第幾’

    vue3 el-pagination 將組件中英文‘goto’ 修改 為&nbs

    這篇文章主要介紹了vue3 el-pagination 將組件中英文‘goto’ 修改 為 中文到‘第幾’,通過實例代碼介紹了vue3項目之Pagination 組件,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • vue中this.$createElement方法的使用

    vue中this.$createElement方法的使用

    這篇文章主要介紹了vue中this.$createElement方法的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue使用gitshot生成gif問題

    vue使用gitshot生成gif問題

    這篇文章主要介紹了vue使用gitshot生成gif問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Vue自動化注冊全局組件腳本分享

    Vue自動化注冊全局組件腳本分享

    這篇文章主要介紹了Vue自動化注冊全局組件腳本,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 使用vscode快速建立vue模板過程詳解

    使用vscode快速建立vue模板過程詳解

    這篇文章主要介紹了使用vscode快速建立vue模板過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 通過vue提供的keep-alive減少對服務器的請求次數(shù)

    通過vue提供的keep-alive減少對服務器的請求次數(shù)

    這篇文章主要介紹了通過vue提供的keep-alive減少對服務器的請求次數(shù),文中給大家補充介紹了vue路由開啟keep-alive時的注意點,需要的朋友可以參考下
    2018-04-04
  • vuex 中插件的編寫案例解析

    vuex 中插件的編寫案例解析

    Vuex 的 store 接受 plugins 選項,這個選項暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數(shù),這篇文章主要介紹了vuex 中插件的編寫案例,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-06-06

最新評論