敲重点!!!以下不一定适用于每个人,请大家先看清楚前后端的实现逻辑
业务需求
上传图片,回显,删除,放大查看等
实现逻辑
这里由于后端考虑到上传后可能并未使用的情况,把普通的直接上传流程改为了以下流程(最后有代码):
步骤一
先调取接口创建一个临时的文件目录,得到一个临时tempDirId。
步骤二
选择图片调上传图片接口,并将第一步得到的tempDirId作为参数传给服务端。
步骤三
通过第二步拿到fileId,前端自己存储,最后数据保存时 将tempDirId和fileId一并提交给服务端。
如果有和我业务实现一致的朋友们,抄代码啦~
<template>
<el-upload
ref="uploadRef"
v-loading="state.loading"
action=""
:accept="state.fileAccept"
list-type="picture-card"
:on-preview="handlePreview"
:on-change="handleUpload"
:show-file-list="true"
:auto-upload="true"
:on-remove="handleRemove"
:http-request="fileUpload"
:file-list="fileListData"
:limit="limit"
class="single-upload"
>
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
<el-dialog v-model="state.dialogVisible">
<img class="w100" w-full :src="state.dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<script setup>
import { reactive, ref } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { fileCreate, fileUploadImgCreate } from "@/api/common";
import { showMessage } from "@/utils/message";
const props = defineProps({
// 用于回显数据列表
fileListData: {
type: Array,
default: () => [],
},
limit: {
type: Number,
default: 1,
},
fileSize: {
type: Number,
default: 20,
},
index: {
type: [Number, String],
default: 0,
},
modelValue: {
type: String,
default: "",
},
checkType: {
type: String,
default: "",
},
tip: {
type: String,
default: "",
},
});
const emit = defineEmits(["uploadSuccess", "update:modelValue"]);
const state = reactive({
loading: false,
fileAccept: ".jpg,.jpeg,.png,.JPG,.JPEG",
fileList: [],
formData: null,
dialogImageUrl: "",
dialogVisible: false,
tempDirId: null, //临时id
});
watch(
() => props.fileListData,
(n, o) => {
state.fileList = n;
}
);
watch(
() => state.fileList,
(n, o) => {
const uploadCards = document.querySelectorAll(".single-upload .el-upload--picture-card");
uploadCards[props.index].style.display = n.length > 0 ? "none" : "inline-flex";
},
{ deep: true }
);
watch(
() => props.modelValue,
newVal => {
if (newVal) {
state.fileList = newVal.split(",").map(item => {
return {
url: item,
};
});
return;
}
state.fileList = [];
},
{ immediate: true }
);
const handlePreview = file => {
state.dialogVisible = true;
state.dialogImageUrl = file.url;
};
const handleRemove = (file, fileList) => {
state.fileList = fileList;
emit("update:modelValue", "");
};
const fileUpload = () => {};
let uploadRef = ref(null);
const validateFile = (file, fileList) => {
const isLimit = file.size / 1024 / 1024 < props.fileSize;
const imageTypeList = ["image/jpeg", "image/jpg", "image/png"];
const isImage = imageTypeList.includes(file.raw.type);
if (!isImage) {
showMessage("上传图片只能是 JPG 或 PNG 或 JPGE 格式!", "error");
fileList.splice(fileList.length - 1, 1);
return false;
}
if (!isLimit) {
showMessage("上传图片大小不能超过 " + props.fileSize + "MB!", "error");
fileList.splice(fileList.length - 1, 1);
return false;
}
return true;
};
const handleUpload = async (file, fileList) => {
const isValid = validateFile(file, fileList);
if (isValid) {
try {
const { result: tempDirId } = await fileCreate();//步骤一的接口
state.tempDirId = tempDirId;
state.loading = true;
state.formData = createFormData(file.raw, state.tempDirId);
const { result } = await fileUploadImgCreate(state.formData);//步骤二的接口
state.fileList = fileList;
emit("uploadSuccess", { ...result, tempDirId });
emit("update:modelValue", result.absoluteUrl);
setTimeout(() => {
state.loading = false;
}, 500);
showMessage("图片上传成功!");
} catch (error) {
// console.error(error);
showMessage("图片上传失败!", "error");
state.loading = false;
}
}
};
const createFormData = (file, tempDirId) => {
const formData = new FormData();
formData.append("image", file);
formData.append("tempDirId", tempDirId);
return formData;
};
defineExpose({
state,
});
</script>
由于本次业务使用不算复杂,所以并未把允许的图片格式等作为动态值判断,大家可以根据自己的实际情况做一些优化。
以上可以直接作为组件使用,到这儿,就搞定啦~~