| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 | 
							- <!-- 文件上传,基于 upload-file 和 upload-image 实现 -->
 
- <template>
 
-   <view class="uni-file-picker">
 
-     <view v-if="title" class="uni-file-picker__header">
 
-       <text class="file-title">{{ title }}</text>
 
-       <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
 
-     </view>
 
-     <view v-if="subtitle" class="file-subtitle">
 
-       <view>{{ subtitle }}</view>
 
-     </view>
 
-     <upload-image
 
-       v-if="fileMediatype === 'image' && showType === 'grid'"
 
-       :readonly="readonly"
 
-       :image-styles="imageStyles"
 
-       :files-list="url"
 
-       :limit="limitLength"
 
-       :disablePreview="disablePreview"
 
-       :delIcon="delIcon"
 
-       @uploadFiles="uploadFiles"
 
-       @choose="choose"
 
-       @delFile="delFile"
 
-     >
 
-       <slot>
 
-         <view class="is-add">
 
-           <image :src="imgsrc" class="add-icon"></image>
 
-         </view>
 
-       </slot>
 
-     </upload-image>
 
-     <upload-file
 
-       v-if="fileMediatype !== 'image' || showType !== 'grid'"
 
-       :readonly="readonly"
 
-       :list-styles="listStyles"
 
-       :files-list="filesList"
 
-       :showType="showType"
 
-       :delIcon="delIcon"
 
-       @uploadFiles="uploadFiles"
 
-       @choose="choose"
 
-       @delFile="delFile"
 
-     >
 
-       <slot><button type="primary" size="mini">选择文件</button></slot>
 
-     </upload-file>
 
-   </view>
 
- </template>
 
- <script>
 
-   import { chooseAndUploadFile, uploadCloudFiles } from './choose-and-upload-file.js';
 
-   import {
 
-     get_file_ext,
 
-     get_extname,
 
-     get_files_and_is_max,
 
-     get_file_info,
 
-     get_file_data,
 
-   } from './utils.js';
 
-   import uploadImage from './upload-image.vue';
 
-   import uploadFile from './upload-file.vue';
 
-   import sheep from '@/sheep';
 
-   let fileInput = null;
 
-   /**
 
-    * FilePicker 文件选择上传
 
-    * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
 
-    * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
 
-    * @property {Object|Array}	value	组件数据,通常用来回显 ,类型由return-type属性决定
 
-    * @property {String|Array}	url	  url数据
 
-    * @property {Boolean}	disabled = [true|false]	组件禁用
 
-    * 	@value true 	禁用
 
-    * 	@value false 	取消禁用
 
-    * @property {Boolean}	readonly = [true|false]	组件只读,不可选择,不显示进度,不显示删除按钮
 
-    * 	@value true 	只读
 
-    * 	@value false 	取消只读
 
-    * @property {Boolean}	disable-preview = [true|false]	禁用图片预览,仅 mode:grid 时生效
 
-    * 	@value true 	禁用图片预览
 
-    * 	@value false 	取消禁用图片预览
 
-    * @property {Boolean}	del-icon = [true|false]	是否显示删除按钮
 
-    * 	@value true 	显示删除按钮
 
-    * 	@value false 	不显示删除按钮
 
-    * @property {Boolean}	auto-upload = [true|false]	是否自动上传,值为true则只触发@select,可自行上传
 
-    * 	@value true 	自动上传
 
-    * 	@value false 	取消自动上传
 
-    * @property {Number|String}	limit	最大选择个数 ,h5 会自动忽略多选的部分
 
-    * @property {String}	title	组件标题,右侧显示上传计数
 
-    * @property {String}	mode = [list|grid]	选择文件后的文件列表样式
 
-    * 	@value list 	列表显示
 
-    * 	@value grid 	宫格显示
 
-    * @property {String}	file-mediatype = [image|video|all]	选择文件类型
 
-    * 	@value image	只选择图片
 
-    * 	@value video	只选择视频
 
-    * 	@value all		选择所有文件
 
-    * @property {Array}	file-extname	选择文件后缀,根据 file-mediatype 属性而不同
 
-    * @property {Object}	list-style	mode:list 时的样式
 
-    * @property {Object}	image-styles	选择文件后缀,根据 file-mediatype 属性而不同
 
-    * @event {Function} select 	选择文件后触发
 
-    * @event {Function} progress 文件上传时触发
 
-    * @event {Function} success 	上传成功触发
 
-    * @event {Function} fail 		上传失败触发
 
-    * @event {Function} delete 	文件从列表移除时触发
 
-    */
 
-   export default {
 
-     name: 'sUploader',
 
-     components: {
 
-       uploadImage,
 
-       uploadFile,
 
-     },
 
-     options: {
 
-       virtualHost: true,
 
-     },
 
-     emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'update:url'],
 
-     props: {
 
-       modelValue: {
 
-         type: [Array, Object],
 
-         default() {
 
-           return [];
 
-         },
 
-       },
 
-       url: {
 
-         type: [Array, String],
 
-         default() {
 
-           return [];
 
-         },
 
-       },
 
-       disabled: {
 
-         type: Boolean,
 
-         default: false,
 
-       },
 
-       disablePreview: {
 
-         type: Boolean,
 
-         default: false,
 
-       },
 
-       delIcon: {
 
-         type: Boolean,
 
-         default: true,
 
-       },
 
-       // 自动上传
 
-       autoUpload: {
 
-         type: Boolean,
 
-         default: true,
 
-       },
 
-       // 最大选择个数 ,h5只能限制单选或是多选
 
-       limit: {
 
-         type: [Number, String],
 
-         default: 9,
 
-       },
 
-       // 列表样式 grid | list | list-card
 
-       mode: {
 
-         type: String,
 
-         default: 'grid',
 
-       },
 
-       // 选择文件类型  image/video/all
 
-       fileMediatype: {
 
-         type: String,
 
-         default: 'image',
 
-       },
 
-       // 文件类型筛选
 
-       fileExtname: {
 
-         type: [Array, String],
 
-         default() {
 
-           return [];
 
-         },
 
-       },
 
-       title: {
 
-         type: String,
 
-         default: '',
 
-       },
 
-       listStyles: {
 
-         type: Object,
 
-         default() {
 
-           return {
 
-             // 是否显示边框
 
-             border: true,
 
-             // 是否显示分隔线
 
-             dividline: true,
 
-             // 线条样式
 
-             borderStyle: {},
 
-           };
 
-         },
 
-       },
 
-       imageStyles: {
 
-         type: Object,
 
-         default() {
 
-           return {
 
-             width: 'auto',
 
-             height: 'auto',
 
-           };
 
-         },
 
-       },
 
-       readonly: {
 
-         type: Boolean,
 
-         default: false,
 
-       },
 
-       sizeType: {
 
-         type: Array,
 
-         default() {
 
-           return ['original', 'compressed'];
 
-         },
 
-       },
 
-       driver: {
 
-         type: String,
 
-         default: 'local', // local=本地 | oss | unicloud
 
-       },
 
-       subtitle: {
 
-         type: String,
 
-         default: '',
 
-       },
 
-     },
 
-     data() {
 
-       return {
 
-         files: [],
 
-         localValue: [],
 
-         imgsrc: sheep.$url.static('/static/img/shop/upload-camera.png'),
 
-       };
 
-     },
 
-     watch: {
 
-       modelValue: {
 
-         handler(newVal, oldVal) {
 
-           this.setValue(newVal, oldVal);
 
-         },
 
-         immediate: true,
 
-       },
 
-     },
 
-     computed: {
 
-       returnType() {
 
-         if (this.limit > 1) {
 
-           return 'array';
 
-         }
 
-         return 'object';
 
-       },
 
-       filesList() {
 
-         let files = [];
 
-         this.files.forEach((v) => {
 
-           files.push(v);
 
-         });
 
-         return files;
 
-       },
 
-       showType() {
 
-         if (this.fileMediatype === 'image') {
 
-           return this.mode;
 
-         }
 
-         return 'list';
 
-       },
 
-       limitLength() {
 
-         if (this.returnType === 'object') {
 
-           return 1;
 
-         }
 
-         if (!this.limit) {
 
-           return 1;
 
-         }
 
-         if (this.limit >= 9) {
 
-           return 9;
 
-         }
 
-         return this.limit;
 
-       },
 
-     },
 
-     created() {
 
-       if (this.driver === 'local') {
 
-         uniCloud.chooseAndUploadFile = chooseAndUploadFile;
 
-       }
 
-       this.form = this.getForm('uniForms');
 
-       this.formItem = this.getForm('uniFormsItem');
 
-       if (this.form && this.formItem) {
 
-         if (this.formItem.name) {
 
-           this.rename = this.formItem.name;
 
-           this.form.inputChildrens.push(this);
 
-         }
 
-       }
 
-     },
 
-     methods: {
 
-       /**
 
-        * 公开用户使用,清空文件
 
-        * @param {Object} index
 
-        */
 
-       clearFiles(index) {
 
-         if (index !== 0 && !index) {
 
-           this.files = [];
 
-           this.$nextTick(() => {
 
-             this.setEmit();
 
-           });
 
-         } else {
 
-           this.files.splice(index, 1);
 
-         }
 
-         this.$nextTick(() => {
 
-           this.setEmit();
 
-         });
 
-       },
 
-       /**
 
-        * 公开用户使用,继续上传
 
-        */
 
-       upload() {
 
-         let files = [];
 
-         this.files.forEach((v, index) => {
 
-           if (v.status === 'ready' || v.status === 'error') {
 
-             files.push(Object.assign({}, v));
 
-           }
 
-         });
 
-         return this.uploadFiles(files);
 
-       },
 
-       async setValue(newVal, oldVal) {
 
-         const newData = async (v) => {
 
-           const reg = /cloud:\/\/([\w.]+\/?)\S*/;
 
-           let url = '';
 
-           if (v.fileID) {
 
-             url = v.fileID;
 
-           } else {
 
-             url = v.url;
 
-           }
 
-           if (reg.test(url)) {
 
-             v.fileID = url;
 
-             v.url = await this.getTempFileURL(url);
 
-           }
 
-           if (v.url) v.path = v.url;
 
-           return v;
 
-         };
 
-         if (this.returnType === 'object') {
 
-           if (newVal) {
 
-             await newData(newVal);
 
-           } else {
 
-             newVal = {};
 
-           }
 
-         } else {
 
-           if (!newVal) newVal = [];
 
-           for (let i = 0; i < newVal.length; i++) {
 
-             let v = newVal[i];
 
-             await newData(v);
 
-           }
 
-         }
 
-         this.localValue = newVal;
 
-         if (this.form && this.formItem && !this.is_reset) {
 
-           this.is_reset = false;
 
-           this.formItem.setValue(this.localValue);
 
-         }
 
-         let filesData = Object.keys(newVal).length > 0 ? newVal : [];
 
-         this.files = [].concat(filesData);
 
-       },
 
-       /**
 
-        * 选择文件
 
-        */
 
-       choose() {
 
-         if (this.disabled) return;
 
-         if (
 
-           this.files.length >= Number(this.limitLength) &&
 
-           this.showType !== 'grid' &&
 
-           this.returnType === 'array'
 
-         ) {
 
-           uni.showToast({
 
-             title: `您最多选择 ${this.limitLength} 个文件`,
 
-             icon: 'none',
 
-           });
 
-           return;
 
-         }
 
-         this.chooseFiles();
 
-       },
 
-       /**
 
-        * 选择文件并上传
 
-        */
 
-       chooseFiles() {
 
-         const _extname = get_extname(this.fileExtname);
 
-         // 获取后缀
 
-         uniCloud
 
-           .chooseAndUploadFile({
 
-             type: this.fileMediatype,
 
-             compressed: false,
 
-             sizeType: this.sizeType,
 
-             // TODO 如果为空,video 有问题
 
-             extension: _extname.length > 0 ? _extname : undefined,
 
-             count: this.limitLength - this.files.length, //默认9
 
-             onChooseFile: this.chooseFileCallback,
 
-             onUploadProgress: (progressEvent) => {
 
-               this.setProgress(progressEvent, progressEvent.index);
 
-             },
 
-           })
 
-           .then((result) => {
 
-             this.setSuccessAndError(result.tempFiles);
 
-           })
 
-           .catch((err) => {
 
-             console.log('选择失败', err);
 
-           });
 
-       },
 
-       /**
 
-        * 选择文件回调
 
-        * @param {Object} res
 
-        */
 
-       async chooseFileCallback(res) {
 
-         const _extname = get_extname(this.fileExtname);
 
-         const is_one =
 
-           (Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) ||
 
-           this.returnType === 'object';
 
-         // 如果这有一个文件 ,需要清空本地缓存数据
 
-         if (is_one) {
 
-           this.files = [];
 
-         }
 
-         let { filePaths, files } = get_files_and_is_max(res, _extname);
 
-         if (!(_extname && _extname.length > 0)) {
 
-           filePaths = res.tempFilePaths;
 
-           files = res.tempFiles;
 
-         }
 
-         let currentData = [];
 
-         for (let i = 0; i < files.length; i++) {
 
-           if (this.limitLength - this.files.length <= 0) break;
 
-           files[i].uuid = Date.now();
 
-           let filedata = await get_file_data(files[i], this.fileMediatype);
 
-           filedata.progress = 0;
 
-           filedata.status = 'ready';
 
-           this.files.push(filedata);
 
-           currentData.push({
 
-             ...filedata,
 
-             file: files[i],
 
-           });
 
-         }
 
-         this.$emit('select', {
 
-           tempFiles: currentData,
 
-           tempFilePaths: filePaths,
 
-         });
 
-         res.tempFiles = files;
 
-         // 停止自动上传
 
-         if (!this.autoUpload) {
 
-           res.tempFiles = [];
 
-         }
 
-       },
 
-       /**
 
-        * 批传
 
-        * @param {Object} e
 
-        */
 
-       uploadFiles(files) {
 
-         files = [].concat(files);
 
-         return uploadCloudFiles
 
-           .call(this, files, 5, (res) => {
 
-             this.setProgress(res, res.index, true);
 
-           })
 
-           .then((result) => {
 
-             this.setSuccessAndError(result);
 
-             return result;
 
-           })
 
-           .catch((err) => {
 
-             console.log(err);
 
-           });
 
-       },
 
-       /**
 
-        * 成功或失败
 
-        */
 
-       async setSuccessAndError(res, fn) {
 
-         let successData = [];
 
-         let errorData = [];
 
-         let tempFilePath = [];
 
-         let errorTempFilePath = [];
 
-         for (let i = 0; i < res.length; i++) {
 
-           const item = res[i];
 
-           const index = item.uuid ? this.files.findIndex((p) => p.uuid === item.uuid) : item.index;
 
-           if (index === -1 || !this.files) break;
 
-           if (item.errMsg === 'request:fail') {
 
-             this.files[index].url = item.path;
 
-             this.files[index].status = 'error';
 
-             this.files[index].errMsg = item.errMsg;
 
-             // this.files[index].progress = -1
 
-             errorData.push(this.files[index]);
 
-             errorTempFilePath.push(this.files[index].url);
 
-           } else {
 
-             this.files[index].errMsg = '';
 
-             this.files[index].fileID = item.url;
 
-             const reg = /cloud:\/\/([\w.]+\/?)\S*/;
 
-             if (reg.test(item.url)) {
 
-               this.files[index].url = await this.getTempFileURL(item.url);
 
-             } else {
 
-               this.files[index].url = item.url;
 
-             }
 
-             this.files[index].status = 'success';
 
-             this.files[index].progress += 1;
 
-             successData.push(this.files[index]);
 
-             tempFilePath.push(this.files[index].fileID);
 
-           }
 
-         }
 
-         if (successData.length > 0) {
 
-           this.setEmit();
 
-           // 状态改变返回
 
-           this.$emit('success', {
 
-             tempFiles: this.backObject(successData),
 
-             tempFilePaths: tempFilePath,
 
-           });
 
-         }
 
-         if (errorData.length > 0) {
 
-           this.$emit('fail', {
 
-             tempFiles: this.backObject(errorData),
 
-             tempFilePaths: errorTempFilePath,
 
-           });
 
-         }
 
-       },
 
-       /**
 
-        * 获取进度
 
-        * @param {Object} progressEvent
 
-        * @param {Object} index
 
-        * @param {Object} type
 
-        */
 
-       setProgress(progressEvent, index, type) {
 
-         const fileLenth = this.files.length;
 
-         const percentNum = (index / fileLenth) * 100;
 
-         const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
 
-         let idx = index;
 
-         if (!type) {
 
-           idx = this.files.findIndex((p) => p.uuid === progressEvent.tempFile.uuid);
 
-         }
 
-         if (idx === -1 || !this.files[idx]) return;
 
-         // fix by mehaotian 100 就会消失,-1 是为了让进度条消失
 
-         this.files[idx].progress = percentCompleted - 1;
 
-         // 上传中
 
-         this.$emit('progress', {
 
-           index: idx,
 
-           progress: parseInt(percentCompleted),
 
-           tempFile: this.files[idx],
 
-         });
 
-       },
 
-       /**
 
-        * 删除文件
 
-        * @param {Object} index
 
-        */
 
-       delFile(index) {
 
-         this.$emit('delete', {
 
-           tempFile: this.files[index],
 
-           tempFilePath: this.files[index].url,
 
-         });
 
-         this.files.splice(index, 1);
 
-         this.$nextTick(() => {
 
-           this.setEmit();
 
-         });
 
-       },
 
-       /**
 
-        * 获取文件名和后缀
 
-        * @param {Object} name
 
-        */
 
-       getFileExt(name) {
 
-         const last_len = name.lastIndexOf('.');
 
-         const len = name.length;
 
-         return {
 
-           name: name.substring(0, last_len),
 
-           ext: name.substring(last_len + 1, len),
 
-         };
 
-       },
 
-       /**
 
-        * 处理返回事件
 
-        */
 
-       setEmit() {
 
-         let data = [];
 
-         let updateUrl = [];
 
-         if (this.returnType === 'object') {
 
-           data = this.backObject(this.files)[0];
 
-           this.localValue = data ? data : null;
 
-           updateUrl = data ? data.url : '';
 
-         } else {
 
-           data = this.backObject(this.files);
 
-           if (!this.localValue) {
 
-             this.localValue = [];
 
-           }
 
-           this.localValue = [...data];
 
-           if (this.localValue.length > 0) {
 
-             this.localValue.forEach((item) => {
 
-               updateUrl.push(item.url);
 
-             });
 
-           }
 
-         }
 
-         this.$emit('update:modelValue', this.localValue);
 
-         this.$emit('update:url', updateUrl);
 
-       },
 
-       /**
 
-        * 处理返回参数
 
-        * @param {Object} files
 
-        */
 
-       backObject(files) {
 
-         let newFilesData = [];
 
-         files.forEach((v) => {
 
-           newFilesData.push({
 
-             extname: v.extname,
 
-             fileType: v.fileType,
 
-             image: v.image,
 
-             name: v.name,
 
-             path: v.path,
 
-             size: v.size,
 
-             fileID: v.fileID,
 
-             url: v.url,
 
-           });
 
-         });
 
-         return newFilesData;
 
-       },
 
-       async getTempFileURL(fileList) {
 
-         fileList = {
 
-           fileList: [].concat(fileList),
 
-         };
 
-         const urls = await uniCloud.getTempFileURL(fileList);
 
-         return urls.fileList[0].tempFileURL || '';
 
-       },
 
-       /**
 
-        * 获取父元素实例
 
-        */
 
-       getForm(name = 'uniForms') {
 
-         let parent = this.$parent;
 
-         let parentName = parent.$options.name;
 
-         while (parentName !== name) {
 
-           parent = parent.$parent;
 
-           if (!parent) return false;
 
-           parentName = parent.$options.name;
 
-         }
 
-         return parent;
 
-       },
 
-     },
 
-   };
 
- </script>
 
- <style lang="scss" scoped>
 
-   .uni-file-picker {
 
-     /* #ifndef APP-NVUE */
 
-     box-sizing: border-box;
 
-     overflow: hidden;
 
-     /* width: 100%; */
 
-     /* #endif */
 
-     /* flex: 1; */
 
-     position: relative;
 
-   }
 
-   .uni-file-picker__header {
 
-     padding-top: 5px;
 
-     padding-bottom: 10px;
 
-     /* #ifndef APP-NVUE */
 
-     display: flex;
 
-     /* #endif */
 
-     justify-content: space-between;
 
-   }
 
-   .file-title {
 
-     font-size: 14px;
 
-     color: #333;
 
-   }
 
-   .file-count {
 
-     font-size: 14px;
 
-     color: #999;
 
-   }
 
-   .is-add {
 
-     /* #ifndef APP-NVUE */
 
-     display: flex;
 
-     /* #endif */
 
-     align-items: center;
 
-     justify-content: center;
 
-   }
 
-   .add-icon {
 
-     width: 57rpx;
 
-     height: 49rpx;
 
-   }
 
-   .file-subtitle {
 
-     position: absolute;
 
-     left: 50%;
 
-     transform: translateX(-50%);
 
-     bottom: 0;
 
-     width: 140rpx;
 
-     height: 36rpx;
 
-     z-index: 1;
 
-     display: flex;
 
-     justify-content: center;
 
-     color: #fff;
 
-     font-weight: 500;
 
-     background: rgba(#000, 0.3);
 
-     font-size: 24rpx;
 
-   }
 
- </style>
 
 
  |