前言
大家好,我是辉夜真是太可爱啦,这是我即将最新开启的组件封装或基于组件库二次封装系列,代码均以 vue2
为例,组件库以 vant
为栗子
组件的封装在工作中使用的用处还是很大的,主要体现在维护的方便,可以将所有固定的代码抽到同一个组件中进行统一管理。
封装目的
这次是实现的是图片上传的功能,由于 vant
组件的 uploader
组件的 v-model
设计不太合理。
一般来说,正常单张图片的值是一个 String
,例如 https://xxxx.jpg
然后一串图片是一个 Array
,例如 ['https://xxxx.jpg' , 'https://xxxx.jpg']
。
然后我们仅通过一个 v-model 即可绑定这个值,无需对图片上传之后值的更新,图片删除之后值的清空,或者是默认值的渲染再做多余的管理,可以大大的提升图片相关功能的开发效率。
后期还可以配合写一个 form 组件,使用 json 生成 form 表单。
Props
- v-model 绑定的图片的值
- max-count 绑定的最大值,1为单张图,大于1为列表
- tips 文件上传提示语
Slot
- defautl 默认插槽,用于修改图片上传的控件
开始封装
先书写 template
板块,关于插槽内的图片上传框的样式请自行修改。
html
<template>
<div class="image-uploader-component-main-box">
<van-uploader
:after-read="afterRead"
:max-size="1024 * 1024 * 10"
@delete="deleteFile"
v-model="fileList"
:max-count="maxCount"
accept="image/png,image/jpeg,image/jpg"
>
<slot>
<div class="upload-box">
<span>图片上传</span>
</div>
</slot>
</van-uploader>
</div>
</template>
接下来,就是封装 js
的部分,由于fileList的格式是固定的,所以接下来需要用一个currentValue来进行绑定,fileList
的数组,在初次渲染的时候,需要将currentValue中的值赋值给fileList,这样子才能保持两边数组的同步。
当然,uploadImage
图片上传的调用行为,请自行修改。
javascript
<script>
export default {
name: 'ImageUploader',
props: {
value: {
type: [Array, String],
default: '',
},
maxCount: {
type: Number,
default: 1,
},
},
data() {
return {
currentValue: this.value,
fileList: [],
isInit: false, // 将一开始的默认值赋值给 fileList
}
},
watch: {
value: {
handler(newValue) {
if (!this.isInit) {
if (this.maxCount === 1) {
this.fileList = [
{
url: newValue,
status: 'success',
message: '上传成功',
},
]
} else {
newValue.forEach((item) => {
this.fileList.push({
url: item,
status: 'success',
message: '上传成功',
})
})
}
}
this.currentValue = newValue
},
},
},
methods: {
afterRead(file) {
this.isInit = true
file.status = 'uploading'
file.message = '上传中...'
const formData = new FormData()
formData.append('file', file.file)
uploadImage(formData)
.then((res) => {
if (res.url) {
file.status = 'success'
file.message = '上传成功'
file.url = res // FIXME: 这里上传不做全地址处理了
if (this.maxCount === 1) {
this.currentValue = res.url
} else {
this.currentValue.push(res.url)
}
this.$emit('input', this.currentValue)
} else {
file.status = 'failed'
file.message = '上传失败'
this.$toast.fail(res.error)
}
})
.catch(() => {
file.status = 'failed'
file.message = '上传失败'
})
},
deleteFile(file) {
if (this.maxCount === 1) {
this.currentValue = ''
} else {
this.currentValue.splice(
this.currentValue.findIndex((x) => x === file.url),
1
)
}
this.$emit('input', this.currentValue)
},
},
}
</script>
至于另外别的 API,此处不再做多余的赘述,仅仅搬运下即可。
组件使用
- 单个使用
javascript
<ImageUploader v-model="imgUrl"></ImageUploader>
data() {
return {
imgUrl: ''
}
},
- 多个使用
ruby
<ImageUploader v-model="imgList" :max-count="5"></ImageUploader>
data() {
return {
imgList: []
}
},