AntDesign上传组件upload二次封装+全局上传hook使用

文章目录

  • 前言
  • a-upload组件二次封装
    • 1. 功能分析
    • 2. 代码+详细注释
    • 3. 使用到的全局上传hook代码
    • 4. 使用方式
    • 5. 效果展示
  • 总结


前言

在项目中,ant-design是我们常用的UI库之一,今天就来二次封装常用的组件a-upload批量上传组件,让它用起来更方便。

a-upload组件二次封装

1. 功能分析

(1)自定义展示区,可按UI规范自定义展示
(2)上传失败,过滤成功的文件,重新赋值展示,不展示失败的文件
(3)上传之前的校验通过配置,在组件内统一管理
(4)通过传入defaultValue prop,进行初始化数据显示
(5)通过传入beforeChange prop,可以根据外部promise上传文件返回的结果,做一些处理

2. 代码+详细注释

// @/components/uploads/index.tsx
<template>
  <a-upload v-model:file-list="state.fileList" :accept="accept" list-type="picture" class="avatar-uploader" multiple :max-count="3" :before-upload="beforeUpload" :customRequest="handlerUploadFile" @change="handlerChange" @remove="handlerRemove">
    <a-button>
      <UploadOutlined />
      选择附件
    </a-button>
    <template #itemRender="{ file, actions }">
      <div class="cd-upload-list">
        <LoadingOutlined class="cd-upload-list-loading" v-if="file.status === 'uploading'" />
        <template v-else>
          <a class="cd-upload-list-item" :href="file?.response || file?.url" target="_blank">
            <img v-if="isImage(file?.response)" class="cd-upload-list-img" :src="file?.response || file?.url" alt="" />
            <FileOutlined v-else class="cd-upload-list-icon" />
            <span class="cd-upload-list-name">{{ file.name }}</span>
          </a>
        </template>
        <a class="cd-upload-list-remove" href="javascript:;" @click="actions.remove"> <DeleteOutlined /></a>
      </div>
    </template>
  </a-upload>
</template>
<script lang="ts" setup>
import { reactive, ref, Ref, watch, PropType } from "vue";
import { message } from "ant-design-vue";
import { UploadOutlined, DeleteOutlined, FileOutlined, LoadingOutlined } from "@ant-design/icons-vue";
import type { UploadProps, UploadChangeParam } from "ant-design-vue";
import type { UploadRequestOption } from "ant-design-vue/es/vc-upload/interface";
/**
 * 类型声明
 */
type ProgressProps = UploadProps["progress"];
type uploadFunProps<T> = Omit<UploadRequestOption<T>, "onSuccess"> &
  Omit<UploadRequestOption<T>, "onError"> & {
    onSuccess: any;
    onError: any;
  };
/**
 * @param data 文件地址
 * 是否是图片
 */
const isImage = (data: string | undefined) => {
  if (!data) return false;
  const fileExtensions = [".png", ".jpg", ".jpeg"];
  return fileExtensions.some((extension) => data.endsWith(extension));
};
/**
 * props
 */
const props = defineProps({
  val: {
    type: Array,
    default: () => [],
  },
  accept: {
    type: String,
    default: ".doc,.docx,.xlsx,.xls,.pdf,.jpg,.png,.jpeg",
  },
  max: {
    type: Number,
    default: 1,
  },
  size: {
    type: Number,
    default: 2,
  },
  defaultValue: {
    type: Array,
    default: () => [],
  },
  format: Function,
  text: {
    type: String,
    default: "",
  },
  params: {
    type: Object,
    default: {},
  },
  beforeChange: {
    type: Function as PropType<(options: any) => Promise<any>>,
  },
});
/**
 * emit
 */
const emit = defineEmits(["update:val", "upload", "remove"]);
/**
 * state
 */
type PreviewState = {
  fileList: UploadProps["fileList"];
  loading: boolean;
  imageUrl: string;
  dataList: any[];
};
const state = reactive<PreviewState>({
  fileList: [],
  loading: false,
  imageUrl: "",
  // 数据文件
  dataList: [],
});
/**
 * 文件状态改变时的回调
 */
const handlerChange = ({ file, fileList }: UploadChangeParam) => {
  const status = file.status;
  if (status === "uploading") {
  }
  if (status === "done") {
  }
  // 上传失败,过滤成功的文件,重新赋值展示
  if (!status || status === "error") {
    const files = fileList.filter((item: any) => item.status === "done");
    state.fileList = files;
  }
};
/**
 * 上传之前检查文件
 * @param file 文件对象
 * @returns boolean
 */
const beforeUpload = (file: File) => {
  const type = file.type.split("/")[1];
  if (props.accept.indexOf(type) === -1) {
    message.error(`请上传${props.accept}格式文件`);
    return false;
  }
  const maxSize = file.size / 1024 / 1024 < props.size;
  if (!maxSize) {
    message.error(`图片大小须小于${props.size}MB`);
    return false;
  }
  return type && maxSize;
};
/**
 * 上传进度
 * @param progressEvent 进度对象
 */
const progress: ProgressProps = {};
/**
 * 上传
 */
const handlerUploadFile = (options: uploadFunProps<unknown>) => {
  props?.beforeChange &&
    props
      .beforeChange({ file: options.file, params: props.params })
      .then((res: any) => {
        message.success(`上传成功`);
        console.log("res777", res);
        if (res?.url) {
          options.onSuccess(res.url, options.file);
        }
      })
      .catch(() => {
        message.error(`上传失败`);
        options.onError();
      });
};
/**
 * 删除文件的回调
 * @param file 文件对象
 */
const handlerRemove = (file: File) => {
  emit("remove", file);
};

/**
 * 初始值
 */
const initValue = (list: string[]) => {
  const file = [] as any[];
  list.forEach((item: string) => {
    file.push({
      status: "done",
      url: item,
    });
  });
  // 更新
  state.fileList = file;
};
watch(
  () => props.defaultValue,
  (val: any) => {
    if (!val || (val && !val.length)) {
      return false;
    }
    initValue(val);
  },
  { immediate: true }
);
</script>
<style lang="scss" scoped>
.cd-upload-list {
  display: flex;
  justify-content: space-between;
  position: relative;
  height: 66px;
  padding: 8px;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  margin-top: 8px;
  .cd-upload-list-loading {
    font-size: 20px;
  }
  .cd-upload-list-item {
    display: flex;
    align-items: center;
    .cd-upload-list-img {
      width: 50px;
      height: 50px;
      margin-right: 10px;
    }
    .cd-upload-list-icon {
      font-size: 20px;
      margin-right: 10px;
    }
    .cd-upload-list-name {
    }
  }
  .cd-upload-list-remove {
    display: flex;
    align-items: center;
    font-size: 20px;
  }
}
</style>

3. 使用到的全局上传hook代码

/**
 * api
 */
import { uploadImage } from "@/api/index";
/**
 * 上传资源全局hook
 * @returns 
 */
export function useUploadImage() {
  const useUploadImageBeforeChange = ({ file, params }: { file: File; params: Record<string, any> }) => {
    return new Promise((resolve, reject) => {
      // 实例化表单对象
      const form = new FormData();
      // 表单添加 files 字段
      for (let key in params) {
        form.append(key, params[key]);
      }
      form.append("multipartFiles", file);
      uploadImage(form)
        .then((res: any) => {
          const result = res && res.length;
          resolve(result ? res[0] : null);
        })
        .catch(() => {
          reject();
        });
    });
  };
  return {
    useUploadImageBeforeChange,
  };
}

4. 使用方式

// 引入组件,以及全局上传hook
import Upload from "@/components/upload/index.vue";
import { useUploadImage } from "@/hook/index";
const { useUploadImageBeforeChange } = useUploadImage();
// 使用
const defaultValue: string[] = ref(["http://xxx.pdf"]);
 <Upload :defaultValue="defaultValue" :size="5" :params="{ fileType: 'xxxxxx' }" :before-change="useUploadImageBeforeChange" />

5. 效果展示

(1)上传
在这里插入图片描述
(2)预览、删除
在这里插入图片描述


总结

接下来会继续分享ant-design常用组件二次封装使用,请持续关注。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第213题打家劫舍II

题目&#xff1a; 题解&#xff1a; class Solution { public:int robRange(vector<int>& nums, int start, int end) {int first nums[start], second max(nums[start], nums[start 1]);for (int i start 2; i < end; i) {int temp second;second max(fi…

Nacos服务注册总流程(源码分析)

文章目录 服务注册NacosClient找看源码入口NacosClient服务注册源码NacosServer处理服务注册 服务注册 服务注册 在线流程图 NacosClient找看源码入口 我们启动一个微服务&#xff0c;引入nacos客户端的依赖 <dependency><groupId>com.alibaba.cloud</groupI…

工作两年后,我如何看待设计模式

在软件工程中&#xff0c;设计模式是经过反复验证的最佳实践&#xff0c;用于解决在软件设计中经常遇到的一类问题。它们为开发者提供了一种通用的解决方案和语言&#xff0c;使得复杂的编程问题得以简化&#xff0c;代码结构更加清晰&#xff0c;可维护性大大提高。简而言之&a…

PostgreSQL 如何优化存储过程的执行效率?

文章目录 一、查询优化1. 正确使用索引2. 避免不必要的全表扫描3. 使用合适的连接方式4. 优化子查询 二、参数传递1. 避免传递大对象2. 参数类型匹配 三、减少数据量处理1. 限制返回结果集2. 提前筛选数据 四、优化逻辑结构1. 分解复杂的存储过程2. 避免过度使用游标 五、事务处…

隐私计算实训营第二期第七课:XGB算法与SGB算法开发实践

隐私计算实训营第二期-第七课 第七课&#xff1a;XGB算法与SGB算法开发实践1 决策树模型1.1 决策树的训练和预测过程1.2 决策树的发展过程 2 GBDT模型2.1 Boosting核心思想2.2 GBDT原理 3 XGB模型3.1 XGB核心思想3.2 XGB优点 3 隐语纵向树模型3.1 数据纵向分割3.2 隐私保护的树…

本地部署到服务器上的资源路径问题

本地部署到服务器上的资源路径问题 服务器端的源代码的静态资源目录层级 当使用Thymeleaf时&#xff0c;在templates的目录下为返回的html页面&#xff0c;下面以两个例子解释当将代码部署到tomcat时访问资源的路径配置问题 例子一 index.html&#xff08;在templates的根目录…

EtherCAT转Profinet网关配置说明第二讲:上位机软件配置

EtherCAT协议转Profinet协议网关模块&#xff08;XD-ECPNS20&#xff09;&#xff0c;不仅可以实现数据之间的通信&#xff0c;还可以实现不同系统之间的数据共享。EtherCAT协议转Profinet协议网关模块&#xff08;XD-ECPNS20&#xff09;具有高速传输的特点&#xff0c;因此通…

安卓安全概述

安卓安全概述 1.Android系统概述2.Android系统安全概述3.Android系统的安全机制应用程序框架安全机制内核安全机制运行环境安全机制 4.Android反编译工具 1.Android系统概述 Android采用层次化系统架构&#xff0c;Google官方公布的标准架构如图所示&#xff0c;自顶而下划分为…

vue事件处理v-on或@

事件处理v-on或 我们可以使用v-on指令&#xff08;简写&#xff09;来监听DOM事件&#xff0c;并在事件触发时执行对应的Javascript。用法&#xff1a;v-on:click"methodName"或click"hander" 事件处理器的值可以是&#xff1a; 内敛事件处理器&#xff1…

【MindSpore学习打卡】应用实践-自然语言处理-基于RNN的情感分类:使用MindSpore实现IMDB影评分类

情感分类是自然语言处理&#xff08;NLP&#xff09;中的一个经典任务&#xff0c;广泛应用于社交媒体分析、市场调研和客户反馈等领域。本篇博客将带领大家使用MindSpore框架&#xff0c;基于RNN&#xff08;循环神经网络&#xff09;实现一个情感分类模型。我们将详细介绍数据…

【数据结构(邓俊辉)学习笔记】高级搜索树01——伸展树

文章目录 1. 逐层伸展1. 1 宽松平衡1. 2 局部性1. 3 自适应调整1. 4 逐层伸展1. 5 实例1. 6 一步一步往上爬1. 7 最坏情况 2. 双层伸展2.1 双层伸展2.2 子孙异侧2.3 子孙同侧2.4 点睛之笔2.5 折叠效果2.6 分摊性能2.7 最后一步 3 算法实现3.1 功能接口3.2 伸展算法3.3 四种情况…

uniapp H5页面设置跨域请求

记录一下本地服务在uniapp H5页面访问请求报跨域的错误 这是我在本地起的服务端口号为8088 ip大家可打开cmd 输入ipconfig 查看 第一种方法 在源码视图中配置 "devServer": {"https": false, // 是否启用 https 协议&#xff0c;默认false"port&q…

vb.netcad二开自学笔记5:ActiveX链接CAD的.net写法

一、必不可少的对象引用 使用activex需要在项目属性中勾选以下两个引用&#xff0c;若找不到&#xff0c;则浏览定位直接添加下面两个文件&#xff0c;可以看到位于cad的安装路径下&#xff0c;图中的3个mgd.dll也可以勾选。 C:\Program Files\Autodesk\AutoCAD 2024\Autodes…

(数据大屏)(Hadoop)基于SSM框架的学院校友管理系统的设计与实现+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

Java线上接口耗时分析神器 Arthas

介绍 程序员的日常&#xff0c;总是离不开“调优”和“排查”。尤其当线上环境出现问题&#xff0c;性能瓶颈把人逼疯。这时候&#xff0c;你就需要一款像 Arthas 这样的神器来救场。 什么是 Arthas&#xff1f; 简单来说&#xff0c;Arthas 是阿里巴巴开源的 Java 诊断工具…

前端八股文 对$nextTick的理解

$nexttick是什么? 获取更新后的dom内容 为什么会有$nexttick ? vue的异步更新策略 (这也是vue的优化之一 要不然一修改数据就更新dom 会造成大量的dom更新 浪费性能) 这是因为 message &#xff08;data&#xff09;数据在发现变化的时候&#xff0c;vue 并不会立刻去更…

学习笔记——动态路由——IS-IS中间系统到中间系统(区域划分)

三、IS-IS区域划分 根据IS-IS路由器邻居关系&#xff0c;可以将IS-IS划分为两个区域——骨干区域和非骨干区域。&#xff08;注意&#xff0c;这里的区域不是上文中提到的Area ID&#xff09;由L2的IS-IS邻居构成的区域为骨干区域&#xff0c;由L1的IS-IS邻居构成的区域为非骨…

c与c++的内存管理

给出内存四个分区名字&#xff1a;栈区、堆区、全局区&#xff08;俗话也叫静态变量区&#xff09;、代码区&#xff08;也叫代码段&#xff09;&#xff08;代码段又分很多种&#xff0c;比如常量区&#xff09; 当然也会看到别的定义如&#xff1a; 两者都正确&#xff0c;记…

Adobe Acrobat添加时间戳服务器

文章目录 前言一、Adobe Acrobat添加时间戳服务器1.打开Adobe Acrobat软件2.点击【菜单】→ 【首选项】3.点击【安全性】→【更多】4.点击【新建】5.输入【名称】→【服务器URL】 前言 一、Adobe Acrobat添加时间戳服务器 1.打开Adobe Acrobat软件 2.点击【菜单】→ 【首选项…

广州佛山中山数据中心机房搬迁公司

随着数据中心的发展和迭代&#xff0c;必然面临数据中心搬迁。数据中心搬迁听来简单&#xff0c;其实涉及诸多方面&#xff0c;如信息迁移的安全性、业务的连续性、搬迁的规范性、方案的可行性、组织的统一性等。友力科技&#xff08;广州&#xff09;有限公司&#xff0c;自原…