el-upload组件封装方案

news2024/7/4 5:20:37

basic-upload.vue——基本上传组件

<template>
  <div class="basic-upload-wrap">
    <el-upload
      ref="uploadRef"
      :file-list="fileList"
      :accept="accept"
      @update:file-list="(data) => emits('update:file-list', data)"
      :http-request="uploadFile"
      :on-exceed="handleExceed"
      :on-progress="handleUpdate"
      :on-success="handleUpdate"
      :on-remove="preDelete"
      v-bind="$attrs"
    >
      <slot>
        <div class="upload-btn-wrapper">
          <el-button class="upload-btn" type="primary">
            <svg-icon name="cloud-upload" />上传文件
          </el-button>
          <div class="prompt" v-if="accept">只支持{{ accept }}的格式文件</div>
        </div>
      </slot>
    </el-upload>
  </div>
</template>

<script setup>
import api from "@/api/documentInfo";
const { uploadFiles, deleteFiles } = api;

const emits = defineEmits(["update:file-list"]);
const props = defineProps({
  fileList: {
    type: Array,
    default: () => [],
  },
  accept: {
    type: String,
  },
});

const uploadRef = ref();
const pendingDels = [];
const controllerMap = {};

async function uploadFile(options) {
  const { file, onProgress, onSuccess, onError } = options;
  const formData = new FormData();
  formData.append("files", file);
  const controller = new AbortController();
  controllerMap[file.uid] = controller;
  const { code, message, data } = await uploadFiles(formData, {
    onUploadProgress: (event) => {
      handleProgress(event, onProgress);
    },
    signal: controller.signal,
  });
  if (code !== "0") {
    onError(message || "上传失败");
  }
  onSuccess(data);
}

let timer;
function handleProgress(event, onProgress) {
  let complete = (event.loaded / event.total) * 100;

  if (complete < 90) {
    event.percent = complete;
    onProgress(event);
    return;
  }
  if (timer) return;

  timer = window.setInterval(() => {
    complete += (100 - complete) * 0.2;
    if (complete > 99 && timer) {
      window.clearInterval(timer);
      timer = null;
    }
    event.percent = complete;
    onProgress(event);
  }, 500);
}

function handleExceed(files, uploadFiles) {
  ElMessage.warning(`最多可上传${uploadFiles.length}个附件`);
}

function handleUpdate(response, uploadFile, uploadFiles) {
  const res = uploadFiles.map((item) => {
    return item.response ? item.response : item;
  });
  emits("update:file-list", res);
}

const preDelete = (file) => {
  controllerMap[file.uid]?.abort?.();
  delete controllerMap[file.uid];
  pendingDels.push(file);
  const updatedFileList = props.fileList.filter((f) => f.uid !== file.uid);
  emits("update:file-list", updatedFileList);
};

const confirmDelete = async () => {
  if (pendingDels.length > 0) {
    const ids = [
      ...new Set(pendingDels.map((item) => item.id).filter((v) => v)),
    ];
    if (!ids.length) return;
    await deleteFiles(ids);
    pendingDels.length = 0;
  }
};

const deleteUnBind = () => {
  pendingDels.push(...props.fileList.filter((v) => !v.kbId));
  confirmDelete();
};

onMounted(() => {
  if (uploadRef.value) {
    uploadRef.value.preDelete = preDelete;
    uploadRef.value.confirmDelete = confirmDelete;
    uploadRef.value.deleteUnBind = deleteUnBind;
  }
});
defineExpose({
  upload: uploadRef,
});
</script>

<style lang="scss" scoped>
.upload-btn-wrapper {
  display: flex;
  align-items: center;
  .prompt {
    display: inline-block;
    color: #88c4f9;
    font-size: 12px;
    margin-left: 8px;
    line-height: 1.2;
  }
}
</style>

list-upload.vue——文件上传列表

<template>
  <div class="list-upload-wrap">
    <basic-upload
      ref="uploadRef"
      :show-file-list="false"
      :file-list="fileList"
      @update:file-list="(data) => emits('update:file-list', data)"
      :accept="accept"
      v-bind="$attrs"
    >
    </basic-upload>

    <ul class="file-list-wrap" v-if="!!fileList.length">
      <li v-for="item in fileList" :key="item.id">
        <div class="item-content">
          <span class="file-name" @click="handlePreview(item)">
            {{ item.documentName || item.name }}
          </span>
          <svg-icon name="close" class="close" @click.stop="handleDel(item)" />
        </div>
        <el-progress
          v-if="item.percentage !== undefined && item.percentage !== 100"
          :percentage="item.percentage"
          :format="(percentage) => ''"
          :text-inside="true"
          :stroke-width="4"
          class="el-upload-list__item-progress"
        >
        </el-progress>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { handlePreview } from "./utils.js";

const emits = defineEmits(["update:file-list"]);
defineProps({
  fileList: {
    type: Array,
    default: () => [],
  },
  accept: {
    type: String,
  },
});

const uploadRef = ref();

const handleDel = (val) => {
  uploadRef.value.upload?.preDelete?.(val);
};

const upload = computed(() => uploadRef.value?.upload);
defineExpose({
  upload,
});
</script>

<style lang="scss" scoped>
.list-upload-wrap {
  width: 100%;
}

.file-list-wrap {
  $hover-color: #0093ff;
  list-style-type: none;
  padding: 0;
  margin-top: 16px;
  max-height: 30vh;
  overflow-y: auto;
  padding-right: 6px;

  li {
    margin-bottom: 20px;
    line-height: 1.2;

    &:last-child {
      margin-bottom: 0;
    }

    .item-content {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .file-name {
      font-family: "Microsoft YaHei", "Microsoft YaHei";
      font-weight: 400;
      font-size: 14px;
      color: #ffffff;
      text-align: left;
      font-style: normal;
      text-transform: none;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      cursor: pointer;
      &:hover {
        color: $hover-color;
      }
    }

    .close {
      font-size: 19px;
      flex: 19px 0 0;
      cursor: pointer;
      &:hover {
        color: $hover-color;
      }
    }

    .el-upload-list__item-progress {
      width: 100%;
      position: relative;
      top: 2px;
      margin-bottom: 12px;
    }
  }
}
</style>

utils.js——预览跳转

import router from "@/router";

// 预览文件
export const handlePreview = (file) => {
  if (!file) {
    return;
  }

  const name = file.documentName;
  const url = (process.env.VUE_APP_FILE_PATH || "") + file.documentUrl;
  if (!name || !url || !router) return;

  const lowerCaseName = name.toLowerCase();
  const suffixName = "." + lowerCaseName.split(".").pop();
  // 1. 处理文本和文档类型
  if ([".txt", ".doc", ".docx", ".pdf"].includes(suffixName)) {
    let routeUrl = router.resolve({
      name: "office-preview",
      query: { url, name },
    });
    window.open(routeUrl.href, "_blank");
    return;
  }
  // 2. 处理图片和视频类型
  if (
    [".png", ".jpg", ".jpeg", ".bmp", ".gif", ".mp4"].includes(
      suffixName
    )
  ) {
    window.open(url, "_blank");
    return;
  }
  // 3. 提示不支持的文件类型
  ElMessage({
    message: "暂不支持该格式文件预览",
    type: "warning",
  });
};

Use

<template>
  <el-form-item label="应急预案:" prop="documentList">
    <list-upload ref="uploadRef" v-model:file-list="ruleForm.documentList"
      :accept="'.doc, .docx, .txt, .pdf, .png, .jpg, .jpeg, .gif'" :limit="20"></list-upload>
  </el-form-item>
</template>

<script setup>
  const uploadRef = ref();

  function close() {
    uploadRef.value.upload?.deleteUnBind();
    dialogVisible.value = false;
  }

  async function submit() {
    const formData = {
      id: ruleForm.value.id,
      documentList: ruleForm.value.documentList,
    };
    await update(formData);
    await uploadRef.value.upload?.confirmDelete();
    ElMessage.success("操作成功");
  }
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1886328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Vue3和Plotly.js打造一个3D图在线展示

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 三维网格图的绘制 应用场景 三维网格图广泛应用于科学可视化、医学成像、工程设计等领域&#xff0c;用于展示复杂的数据结构和空间分布。 基本功能 本代码使用 Plotly.js 库创建了一个交互式三维网格图&am…

IDEA 好用的插件,必备的插件

1. GitToolBox 菜单栏显示git分支信息 2.MyBatisx 快速定位找到sql的xml文件 3.RestfulToolkit-fix 快速定位接口的插件 默认快捷键: CtrlAltN 4.EasyCamelQSM 字符串转驼峰 默认快捷键: Ctrl Alt Q 5.Maven Helper 检查maven冲突&#xff0c;图形化展示maven依赖的插…

非静压模型SWASH学习(8)——三维孤立波在锥形岛屿上的爬坡过程(Runup of solitary waves on a conical island)

三维孤立波在锥形岛屿上的爬坡过程&#xff08;Runup of solitary waves on a conical island&#xff09; 算例简介模型配置网格及参数设置网格与地形初始条件与边界条件数值求解方法输出设置模拟时间 波浪&#xff08;孤立波&#xff09;入射边界的时间序列.bnd文件模拟结果注…

调试支付分回调下载平台证书

之前的原生代码放到webman里面&#xff0c;死活跑不通 没办法&#xff0c;只能用esayWeChat6.7 &#xff08;自行下载&#xff09; 它里面配置要用到平台证书 平台证书又要用到 composer require wechatpay/wechatpay 但是请求接口之前&#xff0c;你先要用到一个临时的平台…

[Python学习篇] Python函数

定义函数 语法&#xff1a;使用关键字 def def 函数名(参数): 代码1 代码2 ...... 调用函数 语法&#xff1a; 函数名(参数) 注意&#xff1a;不同的需求&#xff0c;参数可有可无。在Python中&#xff0c;函数必须先定义后使用 示例&#xff1a; # 定义函数 d…

边缘计算网关在现代工业企业中的作用-天拓四方

随着工业4.0时代的到来&#xff0c;数字化转型已经成为工业企业发展的必然趋势。在这一过程中&#xff0c;边缘计算网关以其独特的优势&#xff0c;正逐渐成为工业企业实现智能化、高效化运营的关键技术。 边缘计算网关是一种部署在网络边缘的设备&#xff0c;它集成了计算、存…

每日一题——Python实现PAT乙级1100 校庆(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码结构和逻辑 时间复杂度分析 空间复杂度分析 总结 我要更强 方法一…

RK3568驱动指南|第十五篇 I2C-第181章使用GPIO模拟I2C驱动

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

一个能让渲染性能提高100倍的办法

GPU 光线追踪是当今的热门话题&#xff0c;所以让我们来谈谈它&#xff01;今天我们将光线追踪一个单个球体。 使用片段着色器。 是的&#xff0c;我知道。并不特别花哨。你可以在 Shadertoy 上搜索并获得数百个示例(https://www.shadertoy.com/results?querysphere)。甚至已…

速通RK3568开发板多网口网线直连测试

开源鸿蒙硬件方案领跑者 触觉智能 本文适用于在EVB3568开发板上进行多网口网线直连。触觉智能的EVB3568主板基于瑞芯微RK3568处理器&#xff0c;采用22nm先进工艺制程&#xff0c;四核A55 CPU&#xff0c;主频高达2.0GHz&#xff0c;支持高达8GB高速LPDDR4&#xff0c;1T算力N…

Linux miniconda 安装tensorflow-gpu遇到找不到GPU问题

背景&#xff1a; Linux Miniconda python3.9 安装步骤 1、 pip install tensorflow-gpu2.8.0 -i https://pypi.tuna.tsinghua.edu.cn/simple 2、报错如下&#xff1a; 更换镜像源&#xff0c;单独安装 pip install tf-estimator-nightly2.8.0.dev2021122109 -i https:/…

【C语言】typedef 关键字

在C语言中&#xff0c;typedef关键字用于给现有的数据类型起一个新的名字。它在提高代码可读性、简化复杂类型声明、增强可维护性方面非常有用。typedef通常用于定义结构体、指针、函数指针以及其他复杂类型。 基本用法 typedef int MyInt; MyInt x 10;在这个例子中&#xf…

42、nginx之nginx.conf

nginx----web服务器 一、nginx http就是apache&#xff0c;在国内很少。 nginx是开源的&#xff0c;是一款高性能&#xff0c;轻量级的web服务软件。 稳定性高&#xff0c;而且版本迭代比较快&#xff08;修复bug速度比较快&#xff0c;安全性快&#xff09; 消耗系统资源…

中日区块链“大比拼”!中国蚂蚁加大区块链押注资本!日本索尼进军加密货币市场!

科技巨头在区块链和加密货币领域的动作越来越频繁。近期&#xff0c;中国金融科技巨头蚂蚁集团进一步加大了在区块链业务上的投资&#xff0c;而日本电子科技巨头索尼集团则正式进军加密货币交易领域。这些举措反映了两国对于区块链和加密资产领域的不同态度和布局。 蚂蚁集团加…

Load Tensor to local Nvidia GPU

0. 安装Nvidia驱动 ubuntu24.04的安装非常简单&#xff0c;在安装界面&#xff0c;选择为"图形化和其他硬件安装驱动"&#xff0c;重启后即有原版Nvidia驱动(如图Nvidia X xxx) 1.确定电脑上是否有NvidiaGPU且安装好Nvidia驱动 import torch print(torch.version…

LInux SSH Server远程代码执行漏洞 (CVE-2024-6387)处理

一、漏洞描述 2024年7月1日&#xff0c;OpenSSH Server中存在的一个RCE远程代码执行漏洞&#xff08;CVE-2024-6387&#xff0c;又被称为regreSSHion&#xff09;细节被公开&#xff0c;该漏洞影响基于glibc的Linux系统上的OpenSSH Server (sshd)。 默认配置下的OpenSSH Serve…

MIX OTP——依赖项和总体项目

在本章中&#xff0c;我们将讨论如何管理 Mix 中的依赖项。 我们的 kv 应用程序已经完成&#xff0c;现在是时候实现处理我们在第一章中定义的请求的服务器了&#xff1a; 但是&#xff0c;我们不会向 kv 应用程序添加更多代码&#xff0c;而是将 TCP 服务器构建为另一个应用程…

Linux系统之安装Firefox浏览器

Linux系统之安装Firefox浏览器 一、Firefox浏览器介绍1.1 Firefox浏览器介绍1.2 Firefox浏览器特点 二、环境介绍二、本次实践环境介绍2.1 环境规划2.2 本次实践介绍 三、安装firefox浏览器3.1 安装epel3.2 检查yum仓库状态3.3 安装Firefox浏览器3.4 查看Firefox版本 四、在命令…

win11电源设置

把钩子去掉以后 win11的电脑关机才有用 否则&#xff0c;关机了&#xff0c;电脑也实际上一直在运行

partition()方法——分割字符串为元组

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 partition()方法根据指定的分隔符将字符串进行分割。如果字符串中包含指定的分隔符&#xff0c;则返回一个3元的元组&#xff0c;第一个为…