效果如下
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>