有时候,ui框架的上传组件不能满足我们的使用需求,就需要自定义一个上传的组件。
<template>
<div class="upload" @click="active">
<slot name="fileSlot">
<!-- 默认内容 -->
</slot>
<input @click.stop ref="inputFile" class="input-file" type="file" :accept="accept" :action="action" @change="onFileChange" />
</div>
</template>
<script>
import { getCurrentInstance, reactive, toRefs, onMounted } from 'vue'
export default {
components: {},
props: {
beforeOpenFileBox: {
type: Function,
default: null
},
accept: {
type: String,
default: () => ''
},
action: {
type: String,
default: () => '#'
},
onChange: {
type: Function,
default: null
}
},
setup(props) {
const { proxy} = getCurrentInstance()
// #region 变量
const state = reactive({})
// #endregion
// #region 生命周期、监听、计算 函数
onMounted(() => {})
// #endregion
// #region 页面操作方法
const active = () => {
if (props.beforeOpenFileBox) {
props.beforeOpenFileBox(openFileBox)
} else {
openFileBox()
}
}
const openFileBox = () => {
const el = proxy.$refs.inputFile
el.click()
}
const onFileChange = e => {
const files = e.target.files
const handleFiles = handleOrigin(files)
if (props.onChange) {
props.onChange(handleFiles[0], handleFiles)
}
e.target.value = null
}
const handleOrigin = files => {
const returnV = []
for (let i = 0; i < files.length; i++) {
const file = files[i]
let obj = {
name: file.name,
raw: file,
size: file.size,
uid: file.lastModified,
type: file.type
}
returnV.push(obj)
}
return returnV
}
// #endregion
return {
...toRefs(state),
active,
openFileBox,
onFileChange,
handleOrigin
}
}
}
</script>
<style lang="less"></style>
<style lang="less" scoped>
.upload {
.input-file {
display: none;
}
}
</style>
<template>
<div>
<GlobalUpload
accept=".jpg,.jepg,.png"
action="#"
:before-open-file-box="beforeOpenUpload"
:on-change="onUploadImage"
>
<template v-slot:fileSlot>
<img class="upload-img" v-if="uploadFileUrl" :src="uploadFileUrl"/>
<div v-else class="upload-box">
<upload-outlined />
<div class="upload-title">上传图片</div>
</div>
</template>
</GlobalUpload>
</div>
</template>
<script>
import GlobalUpload from "@/components/globalUpload";
import { reactive, toRefs } from "vue";
import { notification } from "ant-design-vue";
import { UploadOutlined } from "@ant-design/icons-vue";
export default {
props: {},
components: {
GlobalUpload,
UploadOutlined,
},
setup() {
const state = reactive({
uploadFileUrl: null,
});
// 上传前的拦截操作
const beforeOpenUpload = (pass) => {
console.log("上传前的操作");
pass();
};
// 选择某张图片进行上传
const onUploadImage = (file) => {
if (!file) return;
const isQualified = uploadImageVerify(file); // 验证:图片是否符合要求
if (!isQualified) return;
console.log("file", file);
// 上传图片到云服务器,注意取 file.raw
// TODO.....
console.log("file.raw", file.raw);
getBase64(file.raw, (base64Url) => {
state.uploadFileUrl = base64Url;
});
};
// 获取本地图片的base64,以便临时显示
const getBase64 = (img, callback) => {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result));
reader.readAsDataURL(img);
};
// 验证:图片是否符合要求
const uploadImageVerify = (file) => {
// 判断所传图片的文件类型是否符合jpg、jpeg、png
const isJPG =
file.raw.type === "image/jpg" ||
file.raw.type === "image/jpeg" ||
file.raw.type === "image/png";
if (!isJPG) {
notification.error({
message: "提示",
description: "上传的图片只能是 JPG,JPEG,PNG 格式!",
});
return false;
}
// 判断所传图片的大小是否小于1M
const isLt2M = file.size / 1024 / 1024 <= 1;
if (!isLt2M) {
notification.error({
message: "提示",
description: "上传图片大小不能超过 1MB!",
});
return false;
}
// 判断所传图片名字长度是否超过100,若上传到服务器会重命名则可不考虑
const strCodeLength = getStringByteLength(file.name);
if (strCodeLength > 100) {
notification.error({
message: "提示",
description: `上传图片的名称字节长度不能超过 100! 当前名称长度为 ${strCodeLength}`,
});
return false;
}
return true;
};
// 验证:所传图片名字的长度
const getStringByteLength = (string) => {
if (typeof string !== "string") return;
const length = new Array(string.length)
.fill(null)
.reduce((total, _, i) => {
const charCode = string.charCodeAt(i);
total += charCode > 255 ? 2 : 1;
return total;
}, 0);
return length;
};
return {
...toRefs(state),
beforeOpenUpload,
onUploadImage,
};
}
}
<style lang="less" scoped>
.upload-img {
width: 300px;
height: 300px;
background-color: #e9e9e9;
border: 1px dashed #bbbab9;
object-fit: contain;
cursor: pointer;
}
.upload-box {
width: 300px;
height: 300px;
background-color: #e9e9e9;
display: flex;
align-items: center;
justify-content: center;
color: #8c8c8c;
border: 1px dashed #bbbab9;
cursor: pointer;
.upload-title {
margin-left: 8px;
}
&:hover {
border-color: #ac7a16;
color: #ac7a16;
}
}
</style>
Vue3.x
的写法进行示范;一个
文件;觉得有用的朋友请用你的金手指点一下赞,或者评论留言一起探讨技术!