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

news2024/11/18 0:54:13

文章目录

  • 前言
  • 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.coloradmin.cn/o/1896644.html

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

相关文章

Gartner发布软件供应链安全指南:软件供应链攻击造成的损失将从 2023 年的460亿美元上升到2031年的1380亿美元

软件供应链安全是一个关键的风险和合规性问题&#xff0c;但大多数组织都以分散的方式处理它。缺乏一个包罗万象的框架会遗留安全漏洞。通过实施三支柱框架&#xff0c;安全和风险管理领导者可以确保广泛的保护。 主要发现 对软件供应链的攻击给组织带来重大的安全、监管和运营…

Twitter群发消息API接口的功能?如何配置?

Twitter群发消息API接口怎么申请&#xff1f;如何使用API接口&#xff1f; 为了方便企业和开发者有效地与用户互动&#xff0c;Twitter提供了各种API接口&#xff0c;其中Twitter群发消息API接口尤为重要。AokSend将详细介绍Twitter群发消息API接口的功能及其应用场景。 Twit…

船舶雷达与导航中M7/8防水插座应用优势

船舶雷达与导航系统是船舶安全航行的重要组成部分&#xff0c;而7/8防水插座在这些系统中起着至关重要的作用。其中防水MIN-change 7/8"航空法兰插座成型预铸电缆式、组装式、面板式法兰座、T-型三通可选 7/8防水插座的电气性能 7/8防水插座因其优良的电气性能而被广泛应…

【matlab 路径规划】基于改进遗传粒子群算法的药店配送路径优化

一 背景介绍 本文分享的是一个基于订单合并的订单分配和路径规划联合优化&#xff0c;主要背景是骑手根据客户需求&#xff0c;从药店取药之后进行配送&#xff0c;配送的过程中考虑路径的长度、客户的服务时间窗、车辆的固定成本等要素&#xff0c;经过建模和优化得到最优的配…

收银系统源码-营销活动-幸运抽奖

1. 功能描述 营运抽奖&#xff1a;智慧新零售收银系统&#xff0c;线上商城营销插件&#xff0c;商户/门店在小程序商城上设置抽奖活动&#xff0c;中奖人员可内定&#xff1b; 2.适用场景 新店开业、门店周年庆、节假日等特定时间促销&#xff1b;会员拉新&#xff0c;需会…

【漏洞复现】万户协同办公平台——反序列化

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 万户协同办公平台ezEIP是一个综合信息基础应用平台&#xff0c;…

14-11 2024 年的 13 个 AI 趋势

2024 年的 13 个 AI 趋势 人工智能对环境的影响和平人工智能人工智能支持的问题解决和决策针对人工智能公司的诉讼2024 年美国总统大选与人工智能威胁人工智能、网络犯罪和社会工程威胁人工智能治疗孤独与对人工智能的情感依赖人工智能影响者中国争夺人工智能霸主地位人工智能…

上海时尚新品发布会,可以邀请哪些媒体

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 在上海举办时尚新品发布会时&#xff0c;可以邀请的媒体类型多样&#xff0c;以下是一些建议的媒体类型及其特点&#xff1a; 一、平面媒体 报纸&#xff1a; 《文汇报》&#xff1a;上…

【带你全面了解 RAG,深入探讨其核心范式、关键技术及未来趋势】

文末有福利&#xff01; 大型语言模型&#xff08;LLMs&#xff09;已经成为我们生活和工作的一部分&#xff0c;它们以惊人的多功能性和智能化改变了我们与信息的互动方式。 然而&#xff0c;尽管它们的能力令人印象深刻&#xff0c;但它们并非无懈可击。这些模型可能会产生…

python-图像旋转(赛氪OJ)

[题目描述] 输入一个 n 行 m 列的黑白图像&#xff0c;将它顺时针旋转 9090 度后输出。输入&#xff1a; 第一行包含两个整数 n 和 m&#xff0c;表示图像包含像素点的行数和列数。1≤n≤100&#xff0c;1≤m≤100。 接下来 n 行&#xff0c;每行 m 个整数&#xff0c;表示图像…

【FreeRTOS】同步与互斥通信-有缺陷的互斥案例

目录 同步与互斥通信同步与互斥的概念同步与互斥并不简单缺陷分析汇编指令优化过程 - 关闭中断时间轴分析 思考时刻 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 同步与互斥通信 同步与互斥的概念 一句话理解同步与互斥&#xff1a;我等你用完厕所&#xff0c;我再…

【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频

背景前摇&#xff08;省流可以跳过这部分&#xff09; 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师&#xff0c;给我推荐了Scrapy框架。 我之前学过一些爬虫基础&#xff0c;但是用的是比较常见的BeautifulSoup和Request&#xff0c;于是得到Scrapy这个关键词后&am…

【2023ICPC网络赛I 】E. Magical Pair

当时在做洛谷U389682 最大公约数合并的时候我就想到把每个质因子分解出来然后跑高维前缀和&#xff0c;但是那一道题不是用这个方法&#xff0c;所有我也一直在思考这种做法是不是真的有用。因为昨天通过2024上海大学生程序设计竞赛I-六元组计数这道题我了解到了不少关于原根的…

印章谁在管、谁用了、用在哪?契约锁让您打开手机一看便知

“印章都交给谁在管”、“哪些人能用”、“都有哪些业务在用”…这些既是管理者最关心的印章问题也是影响印章安全的关键要素。但是公司旗下分子公司那么多&#xff0c;各类公章、法人章、财务章、合同章一大堆&#xff0c;想“问”明白很难。 契约锁电子签及印控平台推出“印章…

【FreeRTOS】同步互斥与通信 有缺陷的同步示例

目录 1 同步互斥与通信1.1 同步互斥与通信概述1.2 同步与互斥的概念1.3 同步的例子&#xff1a;有缺陷1.4 freertos.c源码3. 互斥的例子&#xff1a;有缺陷4. 通信的例子&#xff1a;有缺陷5. FreeRTOS的解决方案 1 同步互斥与通信 1.1 同步互斥与通信概述 参考《FreeRTOS入门…

滚动表格(vue版本)【已验证可正常运行】

演示图 注&#xff1a;以下代码来自于GPT4o&#xff1a;国内官方直连GPT4o 代码 <template><div><div class"alarmList-child" ref"alarmList" mouseenter.stop"autoRoll(1)" mouseleave.stop"autoRoll()"><div…

Debezium报错处理系列之第111篇:Can‘t compare binlog filenames with different base names

Debezium报错处理系列之第111篇:Cant compare binlog filenames with different base names 一、完整报错二、错误原因三、解决方法Debezium从入门到精通系列之:研究Debezium技术遇到的各种错误解决方法汇总: Debezium从入门到精通系列之:百篇系列文章汇总之研究Debezium技…

【笔记】redis和session的关系

把这句注释掉之后变成了空指针 新用户/老用户的id都登不进页面

k8s-第四节-Service

Service Service 通过 label 关联对应的 PodServcie 生命周期不跟 Pod 绑定&#xff0c;不会因为 Pod 重创改变 IP提供了负载均衡功能&#xff0c;自动转发流量到不同 Pod可对集群外部提供访问端口集群内部可通过服务名字访问 创建 Service kubectl apply -f service.yamlkub…