封装一个vue3的文件上传组件(拖拽或点击选择文件)

news2024/10/1 21:28:27

1. 效果

在这里插入图片描述

选择文件后:
在这里插入图片描述

2. 代码

<template>
	<div
		class="drop-zone c-normal"
		:class="{
			borderOutline: outline,
		}"
		@dragover.prevent
		@drop.prevent="handleDrop"
		@click="chooseFiles"
	>
		<div v-if="files.length < 1" class="flex items-center justify-center">
			<slot name="blank">
				<div class="f-6">
					<svg-icon icon-class="upload-fill"></svg-icon>
				</div>
			</slot>
		</div>
		<div v-if="files.length < 1">
			<slot name="title" :is-dragging="isDragging">
				<div v-if="!isDragging" class="upload-instructions f6">点击或将文件拖到这里上传</div>

				<div v-else class="dragging-feedback">释放以上传文件</div>
			</slot>
		</div>

		<div class="flex items-center">
			<div v-for="(file, index) in files" :key="index" class="flex file-item pa2 flex-column">
				<div class="f2 pa2">
					<svg-icon icon-class="file" />
				</div>
				<div class="f6 truncate">{{ file.name }}</div>
				<div class="delete-icon" @click.stop="removeFile(index)">
					<svg-icon icon-class="delete" />
				</div>
			</div>
		</div>
		<input ref="fileInput" type="file" multiple style="display: none" @change="inputChange" />
	</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import SvgIcon from '@/components/SvgIcon/SvgIcon.vue';

type Prop = {
	outline?: boolean;
	maxFiles?: number;
	data: any;
	fileChange?: (data: any) => boolean | Promise<boolean>;
};

const props = withDefaults(defineProps<Prop>(), {
	outline: true,
	maxFiles: -1,
	data: [],
	fileChange: () => true,
});

const emits = defineEmits(['update:data']);

const isDragging = ref(false);
const files = computed({
	get() {
		return props.data;
	},
	set: async (val) => {
		const flag = await props.fileChange(val);
		if (val.length > props.maxFiles){
		//:TODO 这里需要自定义一哈
			console.log('最多只能上传'+props.maxFiles+'个文件');
			return
		}
		if (flag === false) {
			return;
		}
		emits('update:data', val);
	},
});
const fileInput = ref<HTMLInputElement>();

const handleDrop = (e: DragEvent) => {
	isDragging.value = false;
	const newFiles: File[] | any = e.dataTransfer?.files || [];
	files.value = [...files.value, ...newFiles];
};

const chooseFiles = () => {
	if (fileInput.value) {
		fileInput.value.click();
	}
};

const inputChange = (e: Event) => {
	const target = e.target as HTMLInputElement;
	const newFiles = target.files || [];
	files.value = [...files.value, ...newFiles];
};

const removeFile = (index: number) => {
	files.value.splice(index, 1);
};
</script>

<style lang="scss" scoped>
/*这里的我用的是外部的公共样式, 实际并没有在组件内, 这里将用到的样式复制在下面了*/
:root{
	--c-normal: #a6a5ad;
}
.flex {
	display: flex;
}
.items-center {
	align-items: center;
}
.justify-center {
	justify-content: center;
}
.c{
	&-normal{
		color: var(--c-normal);
	}
}
.f2 {
	font-size: 2.25rem;
}
.pa2 {
	padding: 0.5rem;
}
.f-6,
.f-headline {
	font-size: 6rem;
}
.truncate {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
/* ↑↑↑ 上面都是引用的公共样式  */
.drop-zone {
	box-sizing: border-box;
	width: 100%;
	height: 100%;
	padding: 20px;
	text-align: center;
	min-height: 150px;
	overflow-y: auto;
	&.borderOutline {
		border: 2px dashed #aaa;
	}
	.dragging-feedback {
		background-color: var(--c-normal);
	}

	.upload-instructions {
		margin-bottom: 10px;
	}
	.file-item {
		position: relative;
		width: 6rem;
		.delete-icon {
			position: absolute;
			top: 5px;
			right: 5px;
			cursor: pointer;
			color: var(--c-normal);
			&:hover {
				color: #1a1a1a;
			}
		}
	}
}
</style>

这个组件引入了 <svg-icon/>这个组件(这里可以替换为自己需要的组件), svg-icon 这个组件的封装可以参考这篇文章: vue3 + vite +ts 封住昂svg-icon组件

这篇文章距离现在时间比较长了, 可能会有一些版本问题, 若遇问题可参考其他相似的文章

如果有什么问题或建议欢迎评论或留言

3. 组件说明

prop参数

name说明默认值
outline外边框true
maxFiles最大文件数(大于-1时生效)-1
data文件数据(双向绑定的)[]
fileChange当文件变化时生效需返回一个boolen或Promise<boolean>类型()=>true

slot

name说明参数
blank文件时显示的图标-
title显示的文字isDragging是否正在拖拽

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

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

相关文章

FLStudio21Mac版flstudio v21.2.1.3430简体中文版下载(含Win/Mac)

给大家介绍了许多FL21版本&#xff0c;今天给大家介绍一款FL Studio21Mac版本&#xff0c;如果是Mac电脑的朋友请千万不要错过&#xff0c;当然我也不会忽略掉Win系统的FL&#xff0c;链接我会放在文章&#xff0c;供大家下载与分享&#xff0c;如果有其他问题&#xff0c;欢迎…

使用Vue.extend( ) 模仿 elementui 创建一个类似 message 消息提示框

提示&#xff1a;记录工作中遇到的需求及解决办法 文章目录 前言一、目录结构二、代码1. 创建 m-Toast.vue 文件2. 创建 global.js 文件3. 在 main.js 文件中导入 global.js 文件4. 在 App.vue 文件中使用 全局方法创建的 组件 前言 在此之前一直不明白Vue.extend( )干什么用的…

PCL 用八叉树完成空间变化检测

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1八叉树构建与变化检测 2.1.2检测变化的点云 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更…

快速排序(C语言实现)

目录 基本概念 Hoare版本 动图演示 思路 代码实现&#xff1a; 性能分析 取Key优化 三数取中法选择基准&#xff08;Median-of-Three Partitioning&#xff09; 实现步骤 代码实现 挖坑法 基本步骤 动图 示例说明 代码实现 前后指针法 动图示范 思路 代码实…

Linux操作系统中docker

1、docker概述 1、什么是docker Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows&#xff08;对于windows不是太友好&#xff09;操作系统的机器上&#xff0c;也可以…

Amazon EC2:权限设置指南,构建安全的云环境

在数字化转型的浪潮中&#xff0c;企业纷纷将业务迁移到云端&#xff0c;以提高灵活性和效率。Amazon Elastic Compute Cloud&#xff08;EC2&#xff09;作为 AWS 的核心服务之一&#xff0c;为企业提供了一个强大的云计算平台。然而&#xff0c;随着云环境的复杂性增加&#…

DHCP 中继器

在实际应用中可能会遇到一个比较大的物理网络中存在多个ip子网&#xff0c;而每个ip子网的主机都需要DHCP服务器来动态分配ip地址&#xff0c;实现的方法有两种: 第一种是在每一个子网中设置DHCP服务器&#xff0c;将其分别为每个子网分配ip地址&#xff0c;但此方法会增加开销…

【Hadoop】【vim编辑器】【~/.bashrc 文件】如何编辑

1. 进入 vim 编辑器 在终端中输入以下命令&#xff1a; vim ~/.bashrc 2. 进入插入模式 打开文件后&#xff0c;你将处于普通模式。在普通模式下&#xff0c;你不能直接编辑文本。 要进入插入模式&#xff0c;请按下 i 键。这时&#xff0c;你应该会看到屏幕底部出现 -- 插…

优化java中 HashMap 的容量](capacity值)

我们很多人都知道&#xff0c;分配比我们所需更多的内存可能会对应用程序的性能产生负面影响。因此&#xff0c;使用带有容量的构造函数创建列表可能会产生很大的不同。 但是&#xff0c;使用Maps时&#xff0c;这个优化步骤可能不是那么简单。在本文中&#xff0c;我们将学习…

鸿蒙OpenHarmony【小型系统基础内核(互斥锁)】子系统开发

互斥锁 基本概念 互斥锁又称互斥型信号量&#xff0c;用于实现对共享资源的独占式处理。当有任务持有时&#xff0c;这个任务获得该互斥锁的所有权。当该任务释放它时&#xff0c;任务失去该互斥锁的所有权。当一个任务持有互斥锁时&#xff0c;其他任务将不能再持有该互斥锁…

Redis 主从复制的实现过程

Redis 主从复制的实现过程 1. 初始同步请求2. 快照生成与发送3. 从服务器载入数据4. 增量同步5. 持续同步与部分重同步 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Redis 的主从复制是一个高效的数据同步机制&#xff0c;主要步骤为以下…

5分钟内不能重复发送验证码!

文章目录 引言校验5分钟内不能重复发送验证码生成验证内容保存验证码到缓存获取缓存验证内容验证短信验证码是否正确数据模型see also引言 防止被恶意攻击,使用需要限制用户获取验证码的频率,例如5分钟内不能重复发送验证码!获取验证码接口,也必须进行签名校验。 使用Red…

单片机STM32 外部中断线的使用笔记

一、STM32外部中断线问题小结 1.1 不同的端口同一PIN 在STM32中&#xff0c;不同的端口&#xff08;如PA、PB、PC等&#xff09;上的相同PIN号&#xff08;如PA1、PB1、PC1&#xff09;可以共用一个外部中断线&#xff08;EXTI_Line&#xff09;。这意味着&#xff0c;虽然这些…

[系列]相关的知识点关联

系列 独立&不相关不相关&正交协方差&互相关相关系数协方差&相关系数余弦系数&内积余弦系数&相关系数滤波&卷积卷积&互相关互相关&内积互相关&归一化互相关

Jenkins入门:从搭建到部署第一个Springboot项目(踩坑记录)

本文讲述在虚拟机环境下(模拟服务器)&#xff0c;使用docker方式搭建jenkins&#xff0c;并部署一个简单的Springboot项目。仅记录关键步骤和遇到的坑&#xff0c;后续再进行细节补充。 一、环境准备和基础工具安装 1. 环境 系统环境为本机vmware创建的Ubuntu24.04。 2. yum…

Unity 3D UGUI 系统一口气讲完!(^U^)ノ~YO

UGUI Canvas 画布 Canvas画布是摆放所有 UI 元素的区域&#xff0c;在场景中创建的所有控件都会自动变为 Canvas游戏对象的子对象。 若场景中没有画布&#xff0c;在创建控件时会自动创建画布。 不论是你主动创建还是被动创建&#xff0c;系统都会自动创建一个名为 EventSys…

使用Python解决数据分析中的相关性分析

目录 1.相关系数基础1.1 使用Pandas计算皮尔逊相关系数1.2 计算物品A与其他物品的相关系数1.3 用户间的相关系数1.4 获取相关系数矩阵 2. 相似度计算的Python实现2.1 欧式距离2.2 余弦相似度2.3 皮尔逊相关系数的手动实现 3. 总结 在数据分析中&#xff0c;相关系数是衡量两个变…

应用案例 | HK-MSR数据记录仪如何计算滑雪时膝盖上的应力?

计算滑雪时膝盖上的应力 阿尔卑斯山高山滑雪运动员在滑雪时对膝盖产生的压力有多大&#xff1f;Thea Waldleben&#xff0c;现任瑞士青年速降赛冠军&#xff0c;在她的 "Maturaarbeit"&#xff08;考试项目&#xff09;中回答了这个问题。通过使用HK-MSR数据记录仪&…

Python OpenCV精讲系列 - 边缘检测深入理解(十三)

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;Python OpenCV精讲⚡️⚡️&#x1f496;&#x1f496; 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计&#xff0c;从基础概念入手&#xff0c;逐步深入到图像处理、特征检测、物体识…

地质工程专业职称申报条件详细解读

一、初级&#xff08;助理&#xff09;地质工程工程师评审条件&#xff1a; 1、理工类或者地质工程类专业毕业 2、专科毕业满3年或本科毕业满1年 3、研究生毕业&#xff0c;从事本专业技术工作&#xff0c;当年内考核认定 二、中级地质工程工程师评审条件&#xff1a; 1、理工…