vue3 input 上传 vuedraggable 实现拖拽排序

效果如下

在这里插入图片描述

input 实现上传功能

通过隐藏 input 元素,点击上传触发 input 点击事件,监听 input change 事件

accept 上传文件的类型
multiple 是否允许上传多个

<template>
	<div class="cursor-pointer" @click="submitUpload">上传</div>
	<img class="w-120px h-120px mt-20px" :src="broker.images" alt="" />
    {/* 隐藏 input 元素,通过点击上传触发 */}
	<input ref="inputRef" class="hidden" accept=".png,.jpeg,.jpg" type="file" @change="handleUploadChange" />
</template>

<script lang="ts" setup>
import { ref } from "vue";
import axios from "axios";

const images = ref('')

const inputRef = ref('');
// 点击上传 触发 input 选择事件
function submitUpload() {
	inputRef.value?.click();
}
async function handleUploadChange(e: Event) {
	const { files } = e.target as HTMLInputElement;
	if (files && files.length >= 1) {
        // 单个上传选中第一个
		const file = files[0];
        // 构造 formData 对象
		const formData = new FormData();
        // 接受文件对象字段为 file
		formData.append("file", file, file?.name);
        // 加入其它字段 scene ,值为 avatar
		formData.append("scene", "avatar");

		const res = await axios.post("/api/file", formData);
		images.value = res.data.data.imageThumb;
        // 清空 input
		if (inputRef.value?.value) {
			inputRef.value.value = "";
		}
	}
}
</script>

上传多个并支持拖拽排序

<template>
	<Draggable
		class="flex flex-wrap draggable"
		:list="images"
		:animation="100"
		item-key="id"
		:force-fallback="true"
		ghost-class="ghost"
	>
		<template #item="{ element, index }">
			<div class="mr-10px mb-10px w-120px h-120px box relative rounded-4px relative">
				<img :src="element.url" class="w-120px h-120px object-cover rounded-4px" />
				<div class="!absolute right-0 top-0 cursor-pointer" @click="handleDelete(index)">X</div>
			</div>
		</template>
		<template #footer>
			<div class="cursor-pointer" @click="submitUpload">上传</div>
		</template>
	</Draggable>
	<input ref="inputRef" class="hidden" accept=".png,.jpeg,.jpg" multiple type="file" @change="handleUploadChange" />
</template>

<script lang="ts" setup>
import { ref } from "vue";
import Draggable from "vuedraggable";
import axios from "axios";

interface Images {
	url: string;
	id: number;
}

// 限制上传的数量
const limit = 4;
const images = ref<Images[]>([]);

function handleDelete(index: number) {
	images.value.splice(index, 1);
}

const inputRef = ref();

function submitUpload() {
	inputRef.value?.click();
}
async function handleUploadChange(e: Event) {
	const { files } = e.target as HTMLInputElement;
	if (files && files.length >= 1) {
		let fileList = Array.from(files);
		if (images.value.length + files?.length > limit) {
			alert("不能超出最大上传数量");
			if (inputRef.value?.value) {
				inputRef.value.value = "";
			}
			return;
		}
		fileList.forEach(async (_, index) => {
			const file = files[index];

			const formData = new FormData();
			formData.append("file", file, file?.name);
			formData.append("scene", "avatar");

			if (images.value.length < limit) {
				const res = await axios.post("/api/file", formData);

				images.value.push({
					id: res.data.data.id,
					url: res.data.data.url,
				});

				if (inputRef.value?.value) {
					inputRef.value.value = "";
				}
			}
		});
	}
}
</script>

简单封装组件

<template>
	<Draggable
		class="flex flex-wrap draggable"
		:list="fileList"
		:animation="100"
		item-key="id"
		:force-fallback="true"
		ghost-class="ghost"
	>
		<template #item="{ element, index }">
			<div class="mr-10px mb-10px w-120px h-120px box relative rounded-4px relative">
				<img :src="element.url" class="w-120px h-120px object-cover rounded-4px" />
				<div class="!absolute right-0 top-0 cursor-pointer" @click="handleDelete(index)">X</div>
			</div>
		</template>
		<template #footer>
			<div v-if="fileList.length < limit" class="cursor-pointer" @click="submitUpload">上传</div>
		</template>
	</Draggable>
	<input ref="inputRef" class="hidden" accept=".png,.jpeg,.jpg" multiple type="file" @change="handleUploadChange" />
</template>

<script lang="ts" setup>
import { ref, computed } from "vue";
import Draggable from "vuedraggable";
import axios from "axios";

defineOptions({ name: "CommonUpload" });

interface Images {
	url: string;
	id: number;
}

interface Props {
	limit?: number;
	fileList: Images[];
}

type Emit = (e: "update:fileList", fileList: Images[]) => void;

const props = withDefaults(defineProps<Props>(), {
	multiple: false,
	accept: ".png,.jpeg,.jpg",
	limit: 1,
	fileList: () => [],
	message: "超出最大数量",
	draggable: false,
});
const emit = defineEmits<Emit>();

const inputRef = ref();
const fileList = computed({
	get() {
		return props.fileList;
	},
	set(value: Images[]) {
		emit("update:fileList", value);
	},
});

function handleDelete(index: number) {
	fileList.value.splice(index, 1);
}

function submitUpload() {
	inputRef.value?.click();
}
async function handleUploadChange(e: Event) {
	const { files } = e.target as HTMLInputElement;
	if (files && files.length >= 1) {
		let fileArr = Array.from(files);
		if (fileList.value.length + files?.length > props.limit) {
			alert("不能超出最大上传数量");
			if (inputRef.value?.value) {
				inputRef.value.value = "";
			}
			return;
		}
		fileArr.forEach(async (_, index) => {
			const file = files[index];

			const formData = new FormData();
			formData.append("file", file, file?.name);
			formData.append("scene", "avatar");

			if (fileList.value.length < props.limit) {
				const res = await axios.post("/api/file", formData);

				fileList.value.push({
					id: res.data.data.id,
					url: res.data.data.url,
				});

				if (inputRef.value?.value) {
					inputRef.value.value = "";
				}
			}
		});
	}
}
</script>

.vue 使用

<template>
	<common-upload v-model:fileList="fileList" :limit="3" />
</template>

<script lang="ts" setup>
import { ref } from "vue";
const fileList = ref([]);
</script>

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

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

相关文章

零代码集成自动化的实现逻辑是什么?

零代码的概念是什么&#xff1f; 零代码平台是一种软件开发工具或平台&#xff0c;非技术人员能够创建和部署应用程序&#xff0c;而无需编写任何代码。它提供了可视化的界面和拖拽式的操作&#xff0c;使用户能够通过简单的配置和组合&#xff0c;以图形化的方式构建应用程序…

vue2常见的语法糖

Vue.js 2 提供了一些语法糖&#xff08;syntactic sugar&#xff09;来简化常见的操作。以下是一些 Vue.js 2 中常用的语法糖&#xff1a; v-bind 简写&#xff1a; <!-- 完整语法 --> <a v-bind:href"url">Link</a><!-- 简写 --> <a :hr…

Jmeter进阶使用:BeanShell实现接口前置和后置操作!

一、背景 我们使用Jmeter做压力测试或者接口测试时&#xff0c;除了最简单的直接对接口发起请求&#xff0c;很多时候需要对接口进行一些前置操作&#xff1a;比如提前生成测试数据&#xff0c;以及一些后置操作&#xff1a;比如提取接口响应内容中的某个字段的值。举个最常用…

nexus制品库的介绍及详细部署使用

一、nexus 介绍 Nexus 是一个强大的仓库管理工具&#xff0c;用于管理和分发 Maven、npm、Docker 等软件包。它提供了一个集中的存储库&#xff0c;用于存储和管理软件包&#xff0c;并提供了版本控制、访问控制、构建和部署等功能。 Nexus 可以帮助开发团队提高软件包管理的效…

构建强大的接口自动化测试框架:Pytest实践指南!

一. 背景 Pytest目前已经成为Python系自动化测试必学必备的一个框架&#xff0c;网上也有很多的文章讲述相关的知识。最近自己也抽时间梳理了一份pytest接口自动化测试框架&#xff0c;因此准备写文章记录一下&#xff0c;做到尽量简单通俗易懂&#xff0c;当然前提是基本的py…

DevEco Studio设置每次进入 是否自动进入上一次的项目

首先 我们第一次创建项目 并不是这个界面 如果我们想在这个界面创建项目的话 可以 点击左上角 File 下的 New 下的 Create Project 这里 我们可以点击左上角 File 选择下面的 Settings… 这个界面就有非常多的配置 然后 我们选择到下图操作的位置 这里有一个Reopen projects…

适用于 Windows 和 Mac 电脑的最佳数据恢复软件

当我们的电脑上的文件被错误删除时&#xff0c;总是很难恢复该文件&#xff0c;或者除非您进行了系统还原&#xff0c;否则一切都会恢复到删除恢复的文件或文件夹之前的状态。 拥有合适的 PC 软件始终可以帮助您改善 PC 用户的体验&#xff0c;而适用于 Windows 10 和 11 的良…

SQL 金额数值转换成中文大写

需求&#xff1a;将金额转换成中文大写格式填入单据合计行&#xff1a; _佰_拾_万_仟_佰_拾_元_角_分 1234567.89 壹佰贰拾叁万肆仟伍佰陆拾柒元捌角玖分 1.函数转换 drop function n2C;CREATE FUNCTION n2C (num numeric(14,2)) RETURNS VARCHAR(20) AS BEGIN …

linux NAT网卡配置static

由于是内网&#xff0c;资料无法拷贝&#xff0c;借助参考资料&#xff0c;整理发出。 镜像安装 基本操作。 查看VM配置 图1&#xff0c;有几个信息。一个是NAT借用了网卡里的VMnet8适配器。 子网IP是从192.168.142.0 子网掩码255.255.255.255&#xff0c;对应下面配置的N…

【云平台】STM32微信小程序阿里云平台汇总——持续更新

【云平台】STM32微信小程序阿里云平台汇总——持续更新 文章目录 前言总结 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 【云平台】STM32微信小程序阿里云平台学习板 【云平台】小白从零开始&#xff1a;小程序阿里云平台控制STM32&#xff08…

WordPress批量上传文章和自动发布文章的方法

专业介绍&#xff1a;WordPress批量上传文章技术解析 在现代数字时代&#xff0c;内容创作是网络存在的驱动力之一。对于博客作者、新闻编辑和内容管理员而言&#xff0c;高效地批量上传文章至WordPress平台是提高工作效率的一个关键方面。WordPress作为最受欢迎的内容管理系统…

48、Flink DataStream API 编程指南(3)- 完整版

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

LeetCode Hot100 101.对称二叉树

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 代码&#xff1a; class Solution {public boolean isSymmetric(TreeNode root) {if(rootnull || (root.leftnull && root.rightnull)) {return true;}//用队列保存节点LinkedList<…

【网络安全】meterpreter攻击实战

1.meterpreter 攻击成功后可以做什么指令&#xff1f; 远程控制命令执行摄像头监控密码获取创建后门用户破坏篡改系统。 2.创建后门用户并开启远程连接&#xff1a; net user zhangsan 123456/add && net localgroup adminstrators zhangsan/add exit run getgul -…

面向对象编程(结合GPT):写给小女朋友的保姆级Python软件开发教程01:pyside6环境的配置和工具的使用

参考&#xff1a; 库的安装和两个在pycharm工具的配置 B站教程&#xff1a;可以练听力&#xff1a;【5个小时 PySide6完全开发指南 使用 Qt 进行 Python GUI 桌面应用开发&#xff08;中英字幕&#xff09;】 CSDN也有配置 安装完以后其实这个就是路径。 打开可以看到pyside…

进阶C语言-字符函数和字符串函数

字符函数和字符串函数 &#x1f388;1.函数介绍&#x1f50e;1.1strlen函数&#x1f52d;1.1.1strlen函数的模拟实现&#x1f4d6;1.计数器法&#x1f4d6;2.递归法&#x1f4d6;3.指针-指针 &#x1f50e;1.2strcpy函数&#x1f52d;1.2.1strcpy函数的模拟实现 &#x1f50e;1…

VGN S99快捷键,说明书

VGN S99快捷键-说明书 按键说明灯光效果常见疑难 按键说明 切换关闭电量指示灯&#xff1a;Fn home 灯光效果 常见疑难

burpsuite issue definitions

https://portswigger.net/burp/documentation/scanner/vulnerabilities-list 先从高危的开始学(四十能学剑&#xff0c;时人无此心)&#xff1a; os command injection todo 未完待续

一些好用的12款前端小插件

1. cropper.js Cropper.js 2.0 是一系列用于图像裁剪的 Web 组件。 官网地址&#xff1a;https://fengyuanchen.github.io/cropperjs/v2/zh/ 2. Vditor Vditor是一款浏览器端的 Markdown 编辑器&#xff0c;支持所见即所得、即时渲染&#xff08;类似 Typora&#xff09;和分…

php请求okx接口获取比特币价格数据、k线数据

php请求okx接口获取比特币价格数据 环境配置请求头、签名设置签名配置代理 全部代码 环境 我本地用的是thinkphp框架和guzzle 安装guzzle composer require guzzlehttp/guzzle 配置请求头、签名 我们需要准备api_key&#xff0c;secret_key&#xff0c;passphrase api_key…