vue3+ts+element-plus封装文件上传、上传图片组件,以及上传进度,如何引入使用

2,636 阅读1分钟

在我们开发项目中,如果遇到文件上传时,这时候我们如果来回使用element-plus的文件上传,页面需要来回引入,这是可以自己进行手动封装文件上传的组件进行调用使用

限制上传类型只能为png、jpg

image.png

限制上传类型只能为pdf

image.png

调用接口上传成功

image.png

1、首先下载element-plus
npm install element-plus --save
2、在main.js引入并全局注册封装的上传文件组件
import { createApp, Vue } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';

const app = createApp(App)
// 文件上传组件
import FileUpload from "@/components/FileUpload/fileIndex.vue";
app.component("FileUpload", FileUpload);
app.use(ElementPlus)
app.mount('#app')
3、封装组件代码:
<template>
	<div class="upload-file">
		<el-upload multiple :action="uploadFileUrl" :data="fileData" :before-upload="handleBeforeUpload"
			:file-list="fileList" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed"
			:on-success="handleUploadSuccess" :on-progress="handleProgress" :show-file-list="false" :headers="headers"
			class="upload-file-uploader" ref="upload">
			<!-- 上传按钮 -->
			<slot>
				<el-button type="primary">选取文件</el-button>
			</slot>
		</el-upload>
		<!-- 上传提示 -->
		<div class="el-upload__tip" v-if="showTip">
			请上传
			<template v-if="fileSize">
				大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
			</template>
			<template v-if="fileType">
				格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
			</template>
			的文件
		</div>
		<!-- 文件列表 -->
		<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"
			v-if="showFileList">
			<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content"
				v-for="(file, index) in fileList">
				<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
					<span class="document">
						{{ getFileName(file.name) }}
					</span>
				</el-link>
				<div class="ele-upload-list__item-content-action">
					<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
				</div>
			</li>
		</transition-group>
		<div class="flex flex-direction justify-center align-center" v-if="upLoading"
			style="position: fixed;top: 0;bottom: 0;left: 0;right: 0;height: 100vh;width: 100vw;z-index: 9999999;background-color: rgba(0,0,0,0.8);">
			<div v-loading="upLoading" element-loading-text="" element-loading-background="rgba(0, 0, 0, 0)"
				style="width: 10px;height: 10px;"></div>
			<span style="font-size: 16px;color: var(--el-color-primary);margin-top: 40px;">上传进度:{{ upProgress }}%</span>
		</div>
	</div>
</template>

<script setup>
import { genFileId ,ElMessage } from 'element-plus'	
import { computed, getCurrentInstance, reactive, ref, watch } from "vue";

const props = defineProps({
	modelValue: [String, Object, Array],
	limit: {
		type: Number,
		default: 0,
	},
	fileSize: {
		type: Number,
		default: 0,
	},
	fileType: {
		type: Array,
		default: ['pdf','png','jpg'],
	},
	// 是否显示提示
	isShowTip: {
		type: Boolean,
		default: false,
	},
	// 超出上传数量时,是否覆盖继续上传
	isExceed: {
		type: Boolean,
		default: false,
	},
	showFileList: {
		type: Boolean,
		default: true,
	},
	//定义上传附件要传递的字段
	source: {
		type: String,
		default: "",
	}
});

const { proxy } = getCurrentInstance();
const emit = defineEmits();
const upload = ref()
const num = ref(0);
const uploadList = ref([]);
// 上传的基准地址
const baseUrl = "";
// 上传接口地址
const uploadFileUrl = "";
// 请求头
const headers = { Authorization: "Bearer " };
const fileList = ref();
const upLoading = ref(false)
const upProgress = ref(0)
const showTip = computed(
	() => props.isShowTip && (props.fileType || props.fileSize)
);
const fileData = reactive({
	'source': props.source
})

watch(
	() => props.modelValue,
	(val) => {
		if (val) {
			let temp = 1;
			// 首先将值转为数组
			const list = Array.isArray(val)
				? val
				: (props.modelValue?.toString().split(","));
			// 然后将数组转为对象数组
			fileList.value = list.map((item) => {
				if (typeof item === "string") {
					item = { name: item, url: item };
				}
				item.uid = item.uid || new Date().getTime() + temp++;
				return item;
			});
		} else {
			fileList.value = [];
			return [];
		}
	},
	{ deep: true, immediate: true }
);

// 上传前校检格式和大小
const handleBeforeUpload = (file) => {
	// 校检文件类型
	if (props.fileType.length) {
		let fileExtension = "";
		if (file.name.lastIndexOf(".") > -1) {
			fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
		}
		const isTypeOk = props.fileType.some((type) => {
			if (file.type.indexOf(type) > -1) return true;
			if (fileExtension && fileExtension.indexOf(type) > -1) return true;
			return false;
		});
		if (!isTypeOk) {
			ElMessage.error(
				`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`
			);
			return false;
		}
	}
	// 校检文件大小
	if (props.fileSize) {
		const isLt = file.size / 1024 / 1024 < props.fileSize;
		if (!isLt) {
			ElMessage.error(`上传文件大小不能超过 ${props.fileSize} MB!`);
			return false;
		}
	}
	// ElMessage.loading("正在上传文件,请稍候...");
	upLoading.value = true;
	num.value++;
	return true;
};

// 文件个数超出
const handleExceed = (files) => {
	if (props.isExceed) {
		upload.value.clearFiles()
		const file = files[0]
		file.uid = genFileId()
		upload.value.handleStart(file)
		upload.value.submit()
	} else {
		ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`);
	}
};

// 上传失败
const handleUploadError = (err) => {
	upload.value = false
	ElMessage.error("上传文件失败");
};

// 上传成功回调
const handleUploadSuccess = (res, file) => {
	if (res.code == 200) {
		upLoading.value = false
		uploadList.value.push({ name: res.fileName, url: res.fileName });
		if (uploadList.value.length === num.value) {
			fileList.value = fileList.value
				.filter((f) => f.url !== undefined)
				.concat(uploadList.value);
			uploadList.value = [];
			num.value = 0;
			emit("update:modelValue", listToString(fileList.value));
			emit("uploadSuccess");
			ElMessage.closeLoading();
		}
	} else {
		ElMessage.error(res.msg);
	}
};

// 上传进度
const handleProgress = (evt, uploadFile, uploadFiles) => {
	upProgress.value = Math.round((evt.percent * 100)) / 100
}

// 删除文件
const handleDelete = (index) => {
	fileList.value.splice(index, 1);
	emit("update:modelValue", listToString(fileList.value));
};

// 获取文件名称
const getFileName = (name) => {
	console.log('name', name);
	if (name.lastIndexOf("/") > -1) {
		return name.slice(name.lastIndexOf("/") + 1);
	} else {
		return "";
	}
};

// 对象转成指定字符串分隔
const listToString = (list, separator) => {
	let strs = "";
	separator = separator || ",";
	for (let i in list) {
		if (undefined !== list[i].url) {
			strs += list[i].url + separator;
		}
	}
	return strs !== "" ? strs.substring(0, strs.length - 1) : "";
};
</script>

<style scoped lang="scss">
.upload-file-uploader {
	margin-bottom: 5px;
}

.upload-file-list .el-upload-list__item {
	padding: 20px 0;
	border: 1px solid #e4e7ed;
	line-height: 2;
	margin-bottom: 10px;
	position: relative;
}

.upload-file-list .ele-upload-list__item-content {
	display: flex;
	justify-content: space-between;
	align-items: center;
	color: inherit;
	padding: 2px 12px;
}

.ele-upload-list__item-content-action .el-link {
	margin-left: 16px;
}
</style>

4、页面中引入使用:
<template>
	<FileUpload v-model="projectFile" :fileType="['png','jpg']" :source="'sys_sys_upload_release'">
		<el-button>+ 上传</el-button>
	</FileUpload>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
const projectFile = ref<any>([])
</script>
<style lang="scss"></style>