| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- <template>
- <view>
- <div class="upload-box">
- <slot>
- <ul>
- <li class="list_img" v-for="(item,index) in showImgList" :key="index" :style="{ width: size + 'rpx', height: size + 'rpx' }">
- <view class="iconfont icondelete" @click="delImg(index)"></view>
- <img :src="item" @click="viewImg(item)" :style="{ width: size + 'rpx', height: size + 'rpx' }" :mode="imgMode" />
- </li>
- <li v-if="showImgList.length < num" class="uploadImg" @click="chooseImage" :style="{ width: size + 'rpx', height: size + 'rpx' }">
- <view class="iconfont icontupiantianjia"></view>
- </li>
- </ul>
- </slot>
- </div>
- </view>
- </template>
- <script>
- import { uploadPic } from '@/api/common'
- export default {
- name: 'app-uploader',
- props: {
- // 传入的图片数组
- imgList: {
- type: Array,
- default: () => []
- },
- manualDel: {
- type: Boolean,
- default: false
- },
- // 图片的尺寸
- size: {
- type: Number,
- default: 200
- },
- // 上传图片数量
- num: {
- type: Number,
- default: 9
- },
- // 压缩图片
- compress: {
- type: Boolean,
- default: true
- },
- // 默认限制图片1.5M,单位为M
- maxSize: {
- type: String,
- default: '1.5M'
- },
- // 是否记录用户的选择记录
- isSave: {
- type: Boolean,
- default: false
- },
- // 记录用户的缓存字段
- saveStr: {
- type: String,
- default: 'chooseImage'
- },
- // 是否转base64 受数据传输长度限制,不建议在组件中使用,如果一定要使用,在返回结果中自己转换
- isBase64: {
- type: Boolean,
- default: false
- },
- multiple: {
- // 是否多图上传
- type: Boolean,
- default: true
- },
- disabled: {
- // 是否只读
- type: Boolean,
- default: false
- },
- // 图片展示模式
- imgMode: {
- type: String,
- default: 'scaleToFill'
- },
- uploadPicApi: {
- type: Function,
- default: uploadPic
- }
- },
- data() {
- return {
- resultData: {},
- showImgList: [],
- base64ImgList: [],
- xssarr: ['data:', 'text', 'txt', 'html', 'js', 'css', 'json', 'exe', 'sql', 'xml', 'doc', 'docx']
- }
- },
- onLoad() {
- if (this.isSave) {
- let str = uni.getStorageSync(this.saveStr)
- if (str !== '') {
- str = str.split(',')
- if (str.length > this.num) {
- str = str.slice(0, this.num)
- }
- this.showImgList = str
- }
- } else {
- uni.removeStorageSync(this.saveStr)
- }
- this.showImgList = [...this.imgList]
- },
- methods: {
- chooseImage: async function() {
- if (this.disabled) {
- return
- }
- let that = this
- let tempFilePaths = await that.getImage()
- await that.blobToBase64(tempFilePaths)
- await that.autoUploadImg()
- await that.postMsg()
- return this.showImgList
- },
- postMsg() {
- this.$emit('update:imgList', this.showImgList)
- this.$emit('chooseImage', this.showImgList)
- this.$emit('resultData', this.resultData)
- },
- autoUploadImg() {
- let that = this
- let base64ImgList = that.base64ImgList
- let params = {}
- if (base64ImgList.length === 1) {
- params['file[content]'] = base64ImgList[0]
- } else {
- base64ImgList.forEach((item, index) => {
- params[`file[${index}][content]`] = item
- })
- }
- return new Promise((resolve, reject) => {
- this.uploadPicApi({
- ...params,
- relative_path: 1
- }).then(res => {
- if (res.code === 1) {
- let imgArr = [res.data.url]
- that.showImgList = [...that.showImgList, ...imgArr]
- that.resultData = res.data
- this.$emit('currentUpload', imgArr)
- this.$emit('getResult', that.showImgList)
- resolve(that.showImgList)
- } else {
- reject(res)
- }
- })
- })
- },
- // 转64
- async blobToBase64(tempFilePaths) {
- let that = this
- that.base64ImgList = []
- let newImgList = [...tempFilePaths]
- for (let i = 0; i < newImgList.length; i++) {
- let blob = newImgList[i]
- // 转64位后可以看文件类型 data:text/
- // #ifdef H5
- // 如果是服务器返回的图片不转base64
- // if (blob.indexOf('blob') !== 0) {
- // that.imgPathList.push(blob)
- // continue
- // }
- await that.h5b64(blob).then(res => {
- let startindex = res.indexOf(':')
- let endindex = res.indexOf('/')
- let type = res.substring(startindex + 1, endindex)
- if (that.xssarr.indexOf(type) === -1) {
- that.base64ImgList.push(res)
- }
- })
- // #endif
- // #ifdef MP-WEIXIN
- // 微信临时文件 http://tmp/wx3a16...
- // if (blob.indexOf('http://tmp/') !== 0) {
- // that.imgPathList.push(blob)
- // continue
- // }
- await that.WEIXINb64(blob).then(res => {
- that.base64ImgList.push(res)
- })
- // #endif
- }
- },
- WEIXINb64(blob) {
- return new Promise((resolve, reject) => {
- uni.getFileSystemManager().readFile({
- filePath: blob, // 选择图片返回的相对路径
- encoding: 'base64', // 编码格式
- success: function(res) {
- let b64str = 'data:image/jpeg;base64,' + res.data
- resolve(b64str)
- },
- fail: function(err) {
- console.log(err)
- }
- })
- })
- },
- h5b64(blob) {
- return fetch(blob, {
- method: 'GET',
- headers: new Headers({
- 'Access-Control-Allow-Origin': '*',
- 'Content-Type': 'text/plain'
- }),
- mode: 'no-cors'
- })
- .then(data => {
- const blob = data.blob()
- return blob
- })
- .then(async blob => {
- let reader = new FileReader()
- reader.readAsDataURL(blob)
- return await new Promise((res, reg) => {
- reader.onloadend = function() {
- const data = reader.result
- res(data)
- }
- })
- })
- },
- // 上传图片
- getImage() {
- let that = this
- let showImgList = that.showImgList
- let count = that.num - showImgList.length
- return new Promise((resolve, reject) => {
- uni.chooseImage({
- count: this.multiple ? count : 1, // 默认9
- sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
- sourceType: ['album', 'camera'], // 从相册选择
- success: async function(res) {
- await that.fileValid(res)
- var tempFilePaths = res.tempFilePaths
- if (that.isSave) {
- uni.setStorageSync(that.saveStr, showImgList.join(','))
- }
- resolve(tempFilePaths)
- }
- })
- })
- },
- // 文件过滤
- fileValid(res) {
- let tempFiles = res.tempFiles
- let tempFilePaths = res.tempFilePaths
- let newtempFiles = []
- let newtempFilePaths = []
- for (let i = 0; i < tempFiles.length; i++) {
- // 大于1.5m就不让上传
- let fileSize = tempFiles[i].size
- if (fileSize > 1024 * 1024 * 1.5) {
- uni.showToast({
- title: '过滤不能大于1.5m的文件',
- duration: 2000,
- icon: 'none'
- })
- continue
- }
- // 文件类型设置
- let spl = tempFiles[i].path.split('.')
- let fileType = spl[spl.length - 1]
- if (fileType && this.xssarr.indexOf(fileType) >= 0) {
- uni.showToast({
- title: '过滤不符合规范文件',
- duration: 2000,
- icon: 'none'
- })
- continue
- }
- newtempFiles.push(tempFiles[i])
- newtempFilePaths.push(tempFilePaths[i])
- }
- res.tempFiles = newtempFiles
- res.tempFilePaths = newtempFilePaths
- // console.log('过滤完了')
- },
- // 删除
- async delImg(index) {
- if (this.disabled) {
- return
- }
- if (this.isSave) {
- uni.setStorageSync(this.saveStr, this.showImgList.join(','))
- }
- if (!this.manualDel) {
- this.showImgList.splice(index, 1)
- } else {
- this.$emit('delImage', index)
- }
- await this.postMsg()
- },
- // 预览
- viewImg(path) {
- uni.previewImage({
- urls: this.showImgList,
- current: path
- })
- },
- // 获取图片信息
- getImageInfo(image, cb) {
- uni.getImageInfo({
- src: image,
- success: function(image) {
- cb(image)
- return image
- }
- })
- },
- // 图片压缩
- compressImage() {
- uni.compressImage({
- src: image,
- quality: 80,
- complete: res => {}
- })
- }
- },
- watch: {
- imgList(val) {
- console.log('监听')
- this.showImgList = [...val]
- }
- }
- }
- </script>
- <style scoped lang="scss">
- .uni-system-preview-image {
- background-color: rgba(0, 0, 0, 0.9);
- .uni-preview-image {
- max-width: 100%;
- }
- }
- .upload-box {
- padding: 0 30px;
- &:first-child {
- padding-left: 0;
- }
- & > ul {
- display: inline-block;
- display: flex;
- flex-wrap: wrap;
- & > li {
- list-style: none;
- width: 210px;
- height: 210px;
- margin: 0 20px 20px 0;
- position: relative;
- background-color: #fff;
- .icondelete {
- font-size: 25px;
- z-index: 999;
- position: absolute;
- right: 0;
- bottom: 0;
- color: #fff;
- padding: 0 2px;
- background-color: rgba(0, 0, 0, 0.45);
- }
- }
- }
- & .list_img {
- & > img {
- width: 100%;
- height: 100%;
- }
- }
- & .uploadImg {
- border: 1px dashed #e5e5e5;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- .upload_ipt {
- position: absolute;
- width: 100%;
- height: 100%;
- /*overflow:hidden;*/
- cursor: pointer;
- opacity: 0;
- }
- .iconfont {
- color: rgb(150, 151, 153);
- font-size: 40px;
- }
- }
- }
- </style>
|