vue3+element-plus上传文件,预览文件

vue3+ts+element-plus上传文件,预览文件

vue3+element-plus上传文件,预览文件_第1张图片
vue3+element-plus上传文件,预览文件_第2张图片
vue3+element-plus上传文件,预览文件_第3张图片

场景:使用element-plus的el-upload标签,手动上传文件,可预览docx,xlsx,pdf,jpg,jpeg,png(本地资源以及网络资源)。

1、使用el-upload标签

vue3+element-plus上传文件,预览文件_第4张图片

检查上传文件的文件格式与大小

vue3+element-plus上传文件,预览文件_第5张图片

上传的附件信息在fileList中,组装接口所需数据进行上传

使用docx-preview插件预览docx类型的文件

vue3+element-plus上传文件,预览文件_第6张图片
vue3+element-plus上传文件,预览文件_第7张图片

使用xlsx插件预览xlsx文件

vue3+element-plus上传文件,预览文件_第8张图片
vue3+element-plus上传文件,预览文件_第9张图片
vue3+element-plus上传文件,预览文件_第10张图片
这里遇到了问题,引入xlsx插件的时候出现"export ‘default’ (imported as ‘XLSX’) was not found in 'xlsx’报错
解决:

直接将import XLSX from 'xlsx’改为import * as XLSX from 'xlsx/xlsx.mjs’即可

图片预览

vue3+element-plus上传文件,预览文件_第11张图片
vue3+element-plus上传文件,预览文件_第12张图片

pdf预览

vue3+element-plus上传文件,预览文件_第13张图片
vue3+element-plus上传文件,预览文件_第14张图片

完整代码

<template>
  <el-dialog
    v-model="uploadDialogVisible"
    title="上传"
    width="800px"
    label-width="100px"
    @close="uploadDialogVisible = false"
  >
    <el-upload
      ref="uploadRef"
      v-model:file-list="fileList"
      class="upload-demo"
      drag
      :multiple="true"
      :auto-upload="false"
      :accept="props.allowType"
      :limit="props.limit"
      :before-upload="beforeAvatarUpload"
      :on-preview="previewFun"
    >
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">
        <em>选择文件</em>
      </div>
      <template #tip>
        <div class="el-upload__tip">
          支持格式:{{ props.allowType }};限制大小{{ props.size }}M
        </div>
      </template>
    </el-upload>
    <template #footer>
      <span class="dialog-footer">
        <el-button round @click="cancelFun">取消</el-button>
        <el-button round type="primary" @click="submitFun">确定</el-button>
        <el-button round type="primary" @click="getFileList"
          >获取文件</el-button
        >
      </span>
    </template>
  </el-dialog>
  <!-- 查看 -->
  <viewer ref="fileViewerRef" :dialog-doc="dialogDoc" />
</template>
<script setup lang="ts">
/* eslint-disable */
import { defineProps, defineEmits, ref, reactive, computed } from "vue";
import type {
  FormInstance,
  UploadProps,
  UploadUserFile,
  UploadRawFile
} from "element-plus";
import { ElMessage } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
import { api } from "@/api";
import SparkMD5 from "spark-md5";
import Viewer from "./viewer.vue";

interface Props {
  uploadDialogVisible?: boolean;
  allowType?: string;
  limit?: number;
  fileList?: any;
  size?: number;
  fileExtendId?: string;
  fileCategory?: number;
  folderName?: string;
  systemSource?: string;
  uploadUrl?: string;
}
//props
const props: any = withDefaults(defineProps<Props>(), {
  uploadDialogVisible: false,
  allowType: "doc,docx,jpg,jpeg,png,xlsx,pdf",
  limit: 5,
  size: 5,
  fileExtendId: "",
  fileCategory: 2,
  folderName: "ceshi",
  systemSource: "ceshi",
  uploadUrl: "http://192.168.188.3:7001"
});

const uploadDialogVisible = computed(() => {
  return props.uploadDialogVisible;
});

const uploadRef = ref<FormInstance>();
const fileList = ref(([] as any));
const fileExtendId = ref("682D0E35121A4D4E831714CDACD5A18E");

const beforeAvatarUpload = () => {
  const flag = ref(true);
  fileList.value.forEach((item: any) => {
    console.log(item);
    const type = item.name.split(".")[1];
    if (props.allowType.indexOf(type) == -1) {
      ElMessage({
        type: "error",
        message: `格式错误,支持格式:${props.allowType}!`
      });
      flag.value = false;
      return;
    } else if (item.size / 1024 / 1024 > props.size) {
      ElMessage.error(`文件最大${props.size}MB!`);
      flag.value = false;
      return;
    }
  });
  return flag.value;
};

//取消
const cancelFun = () => {
  emit("cancel", false);
};

const submitFun = (formEl: FormInstance | undefined) => {
  // 判断是否有文件需要上传
  if (fileList.value.length) {
    const flag = beforeAvatarUpload();
    if (!flag) {
      return;
    }
    // 组合数据
    const params = new FormData();
    const fileConfigs: any = [];
    if (fileList.value.length) {
      fileList.value.forEach((item: any, index: number) => {
        // 判断一下是不是新上传的
        if (item.id) {
          fileConfigs.push({
            fileId: item.id,
            fileName: item.name,
            sort: ++index, // 必须从1开始,依次12345往下
            md5: ""
          });
        } else {
          params.append("file", item.raw);
          const spark = new SparkMD5.ArrayBuffer();
          spark.append(item.raw);
          const md5 = spark.end();
          fileConfigs.push({
            fileId: "",
            fileName: item.name,
            sort: ++index, // 必须从1开始,依次12345往下
            md5: md5
          });
      };
      }
      );
    }
    params.append("fileExtendId", fileExtendId.value);
    params.append("fileCategory", props.fileCategory);
    params.append("folderName", props.folderName);
    params.append("systemSource", props.systemSource);
    params.append("FileConfigs", JSON.stringify(fileConfigs));
    console.log(params);
    api.adminCenter.UploadFiles(params).then((res: any) => {
      if (res.status.code == 200) {
        fileExtendId.value = res.result.fileExtendId;
        // 拿到了组合id,继续做业务
      }
    });
  } else {
    console.log("直接做业务");
  }
};

// 获取附件
const getFileList = () => {
  api.adminCenter
    .GetFileList({
      fileExtendIds: fileExtendId.value
    })
    .then((res: any) => {
      if (res.status.code == 200) {
        res.result.forEach((ele: any) => {
          ele.name = ele.fileOldName;
          fileList.value.push(ele);
        });
      } else {
      }
    });
};

let dialogDoc: any = ref(false);
const fileViewerRef = ref<any>(null);
const previewFun = (file: any) => {
  console.log(file.halfPath);
  dialogDoc.value = true;
  let data = file;
  // 上传过的文件组成完成的网络路径
  if (file.halfPath) {
    data.src = `${props.uploadUrl}${file.halfPath}`;
  }

  const suffix = data.name.split(".")[1];
  if (suffix == "docx") {
    fileViewerRef.value?.viewDocx(data);
  } else if (suffix == "xlsx") {
    fileViewerRef.value?.viewXlsx(data);
  } else if (suffix == "jpg" || suffix == "jpeg" || suffix == "png") {
    fileViewerRef.value?.viewImg(data);
  } else if (suffix == "pdf") {
    fileViewerRef.value?.viewPdf(data);
  }
};
// 声明emit
const emit = defineEmits(["cancel"]);
</script>

<template>
  <!-- doc -->
  <el-dialog
    v-model="dialogDocxValue"
    :title="dialogTitle"
    width="80%"
    @close="dialogDocxClose"
  >
    <div ref="docxRef" class="word-div"></div>
  </el-dialog>

  <!-- xlsx -->
  <el-dialog
    v-model="dialogXlsxValue"
    :title="dialogTitle"
    width="80%"
    @close="dialogXlsxClose"
  >
    <div ref="xlsxRef" class="xlsx-div">
      <el-tabs v-model="activeName" type="border-card">
        <el-tab-pane
          v-for="(item, index) in excelSheet"
          :key="index"
          :label="item.name"
          :name="item.name"
        >
          <div class="table" v-html="item.html"></div>
        </el-tab-pane>
      </el-tabs>
    </div>
  </el-dialog>

  <!-- 图片 -->
  <el-dialog
    v-model="dialogImgValue"
    :title="dialogTitle"
    width="80%"
    @close="dialogImgClose"
  >
    <div class="img-div">
      <el-image :src="fileData.src" :alt="fileData.fileOldName" />
    </div>
  </el-dialog>

  <!-- pdf -->
  <el-dialog
    v-model="dialogPdfValue"
    :title="dialogTitle"
    width="80%"
    @close="dialogPdfClose"
  >
    <div class="pdf-div">
      <iframe
        id="pdfRef"
        :src="iframeUrl"
        frameborder="0"
        style="width: 100%; height: 99%"
      ></iframe>
    </div>
  </el-dialog>
</template>
<script setup lang="ts">
import axios from "axios";
import { defineProps, ref, reactive, computed, nextTick } from "vue";
import { renderAsync } from "docx-preview";
import * as XLSX from "xlsx";
interface Props {
  dialogDocx?: boolean;
  dialogXlsx?: boolean;
  dialogTitle?: string;
}
const props: any = withDefaults(defineProps<Props>(), {
  dialogDocx: false,
  dialogTitle: "",
  dialogXlsx: false
});
const dialogDocxValue: any = ref(false);
let dialogTitle = computed(() => {
  return props.dialogTitle;
});
const fileHtml = ref("");

const docxRef = ref<any>();
// doc 文档预览
const viewDocx = (data: any) => {
  docxRef.value = "";
  dialogDocxValue.value = true;
  console.log(data);
  if (data.src) {
    // 已上传的文件
    axios({
      url: data.src,
      method: "get",
      responseType: "blob"
    }).then((res) => {
      console.log(res);
      if (res.status == 200) {
        const content = res.data;
        const blob = new Blob([content]);
        nextTick(() => {
          dialogDocxValue.value = true;
          renderAsync(blob, docxRef.value);
          dialogTitle = data.fileOldName || data.name;
        });
      }
    });
  } else {
    // 本地文件
    const blob = new Blob([data.raw]);
    nextTick(() => {
      dialogDocxValue.value = true;
      renderAsync(blob, docxRef.value);
      dialogTitle = data.fileOldName || data.name;
    });
  }
};
const dialogXlsxValue: any = ref(false);
const excelSheet: any = ref([]);
const activeName = ref("");
const dialogDocxClose = () => {
  dialogDocxValue.value = false;
  docxRef.value = "";
};
// xlsx 预览
const viewXlsx = (data: any) => {
  dialogXlsxValue.value = true;
  console.log(data);
  if (data.src) {
    axios({
      url: data.src,
      method: "get",
      responseType: "blob"
    }).then((res) => {
      console.log(res);
      if (res.status == 200) {
        const content = res.data;
        // const blob = new Blob(content);
        const reader = new FileReader();
        reader.readAsArrayBuffer(content);
        reader.onload = function (loadEvent: any) {
          const arrayBuffer = loadEvent.target["result"];
          const workbook = XLSX.read(new Uint8Array(arrayBuffer), {
            type: "array"
          });
          const list = [];
          const sheetNames = workbook.SheetNames;
          activeName.value = sheetNames[0];
          for (const p of sheetNames) {
            let html = "";
            try {
              html = XLSX.utils.sheet_to_html(workbook.Sheets[p]);
            } catch (e) {
              html = "";
            }
            const map = {
              name: p,
              html: html
            };
            list.push(map);
          }
          excelSheet.value = list;
          dialogTitle = data.fileName || data.name;
        };
      }
    });
  } else {
    const blob = new Blob([data.raw]);
    const reader = new FileReader();
    reader.readAsArrayBuffer(blob);
    reader.onload = function (loadEvent: any) {
      const arrayBuffer = loadEvent.target["result"];
      const workbook = XLSX.read(new Uint8Array(arrayBuffer), {
        type: "array"
      });
      const list = [];
      const sheetNames = workbook.SheetNames;
      activeName.value = sheetNames[0];
      for (const p of sheetNames) {
        let html = "";
        try {
          html = XLSX.utils.sheet_to_html(workbook.Sheets[p]);
        } catch (e) {
          html = "";
        }
        const map = {
          name: p,
          html: html
        };
        list.push(map);
      }
      excelSheet.value = list;
      dialogTitle = data.fileName || data.name;
    };
  }
};

const dialogXlsxClose = () => {
  dialogXlsxValue.value = false;
  excelSheet.value = "";
  activeName.value = "";
};
const fileData: any = ref({});
const dialogImgValue: any = ref(false);
// 图片预览
const viewImg = (data: any) => {
  if (data.src) {
    // 已上传的图片
    fileData.value = data;
  } else {
    // 本地图片
    const freader = new FileReader();
    freader.readAsDataURL(data.raw);
    freader.onload = (e: any) => {
      fileData.value = {
        src: e.target.result,
        id: new Date(),
        fileName: data.fileOldName || data.name
      };
    };
  }
  dialogTitle = data.fileOldName || data.name;
  dialogImgValue.value = true;
};
const dialogImgClose = () => {
  dialogImgValue.value = false;
};

const dialogPdfValue: any = ref(false);
const iframeUrl: any = ref("");
const pdfRef = ref<any>();
const viewPdf = (data: any) => {
  if (data.src) {
    axios({
      url: data.src,
      method: "get",
      responseType: "blob"
    }).then((res) => {
      if (res.status == 200) {
        // 把文件流转化为url
        iframeUrl.value = URL.createObjectURL(res.data);
        dialogPdfValue.value = true;
      }
    });
  } else {
    iframeUrl.value = URL.createObjectURL(data.raw);
    dialogPdfValue.value = true;
  }
};
const dialogPdfClose = () => {
  dialogPdfValue.value = false;
};
defineExpose({
  viewDocx,
  viewXlsx,
  viewImg,
  viewPdf
});
</script>
<style scoped lang="scss">
.word-div {
  height: calc(70vh);
  overflow: auto;
}
.xlsx-div {
  height: calc(70vh);
  overflow: auto;
}
.img-div {
  height: calc(70vh);
  overflow: auto;
  img {
    width: 100%;
    height: 100%;
  }
}
.pdf-div {
  height: calc(70vh);
  overflow: auto;
}
</style>
<style lang="scss">
.xlsx-div {
  .table-html-wrap table {
    border-right: 1px solid #fff;
    border-bottom: 1px solid #e8eaec;
    border-collapse: collapse;
    // margin: auto;
  }

  .table-html-wrap table td {
    border-left: 1px solid #e8eaec;
    border-top: 1px solid #e8eaec;
    white-space: wrap;
    text-align: left;
    min-width: 100px;
    padding: 4px;
  }

  table {
    border-top: 1px solid #ebeef5;
    border-left: 1px solid #ebeef5;
    width: 100%;
    // overflow: auto;

    tr {
      height: 44px;
    }

    td {
      min-width: 200px;
      max-width: 400px;
      padding: 4px 8px;
      border-right: 1px solid #ebeef5;
      border-bottom: 1px solid #ebeef5;
    }
  }

  .el-tabs--border-card > .el-tabs__content {
    overflow-x: auto;
  }
}
</style>

你可能感兴趣的:(vue3,前端,typescript,vue)