vue3 input 上传 vuedraggable 实现拖拽排序

7 篇文章 0 订阅

效果如下

在这里插入图片描述

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>

上传后清空 input 值,否则连续上传同一张图不会触发 change 事件

if (inputRef.value?.value) {
	inputRef.value.value = "";
}

上传多个并支持拖拽排序

<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>

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值