You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
3.2 KiB
Vue
152 lines
3.2 KiB
Vue
<template>
|
|
<view class="ch-image-uploader">
|
|
<ch-flex class="img-list" justify="center" items="center" @click.native="handleSelect">
|
|
<block v-for="item in imageList" key="item.name">
|
|
<view class="img-item" data-type="img">
|
|
<uni-icons type="clear" class="del-btn" size="50" v-if="showClose" data-type="delete" @click="delImg(item.name)"></uni-icons>
|
|
<image :src="item.path" mode="aspectFit"></image>
|
|
</view>
|
|
</block>
|
|
<view class="select-btn" data-type="btn" v-if="showBtn">
|
|
<uni-icons type="plusempty" data-type="add" size="70"/>
|
|
</view>
|
|
</ch-flex>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup name="ch-image-uploader">
|
|
import { ref, defineProps, reactive, toRefs, watchEffect, watch, computed, defineEmits, defineExpose } from 'vue';
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
defineExpose({
|
|
upload
|
|
})
|
|
|
|
const props = defineProps({
|
|
limit: {
|
|
type: Number,
|
|
default: 8
|
|
},
|
|
extList: {
|
|
type: Array,
|
|
default: () => ['png', 'jepg', 'jpg']
|
|
},
|
|
sourceType: {
|
|
type: Array,
|
|
default: () => ['album', 'camera']
|
|
},
|
|
showClose: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
url: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
modelValue: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
|
|
const data = reactive({
|
|
imageList: []
|
|
})
|
|
|
|
const { imageList } = toRefs(data);
|
|
|
|
const showBtn = computed(() => {
|
|
return (props.limit <= 1 && imageList.value.length <= 0)
|
|
|| (props.limit > 1 && imageList.value.length <= props.limit - 1)
|
|
})
|
|
|
|
watchEffect(() => {
|
|
imageList.value = props.modelValue;
|
|
})
|
|
|
|
function handleSelect (e) {
|
|
const type = e.target.dataset.type || false;
|
|
|
|
if (!type || (type === 'img' && props.limit > 1)) return;
|
|
if (type === 'delete') {
|
|
delImg();
|
|
return;
|
|
}
|
|
|
|
uni.chooseImage({
|
|
count: props.limit,
|
|
extension: props.extList,
|
|
sourceType: props.sourceType,
|
|
success: (res) => {
|
|
if (props.limit > 1) {
|
|
const newImageList = filterImage(res.tempFiles)
|
|
imageList.value = [...imageList.value, ...newImageList]
|
|
} else {
|
|
imageList.value = res.tempFiles
|
|
}
|
|
emit('update:modelValue', imageList.value)
|
|
},
|
|
fail: (res) => {
|
|
uni.showToast({
|
|
icon: 'error',
|
|
title: '选择失败'
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
// 上传
|
|
function upload () {
|
|
const files = formatImageFiles(imageList.value);
|
|
console.log(files)
|
|
return new Promise((resolve, reject) => {
|
|
uni.uploadFile({
|
|
url: props.url,
|
|
files,
|
|
name: 'task-image',
|
|
success: (uploadFileRes) => {
|
|
uni.showToast({
|
|
title: '上传成功'
|
|
})
|
|
resolve(uploadFileRes)
|
|
},
|
|
fail: (error) => {
|
|
uni.showToast({
|
|
icon: 'fail',
|
|
title: '上传失败'
|
|
})
|
|
reject(error)
|
|
console.log('上传失败', error)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
function delImg (delName) {
|
|
imageList.value = imageList.value.filter(item => item.name !== delName)
|
|
}
|
|
|
|
// 格式化image列表
|
|
function formatImageFiles (fileList) {
|
|
return imageList.value.map(item => {
|
|
return {
|
|
name: item.name,
|
|
size: item.size,
|
|
uri: item.path,
|
|
file: item
|
|
}
|
|
})
|
|
}
|
|
|
|
// 过滤重复的图片
|
|
function filterImage (images) {
|
|
return images.filter(item => {
|
|
const result = imageList.value.find(img => img.name === item.name );
|
|
return !result;
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import './ch-image-uploader.scss';
|
|
</style> |