init
This commit is contained in:
		
							
								
								
									
										67
									
								
								uni_modules/uni-file-picker/changelog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								uni_modules/uni-file-picker/changelog.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
## 1.0.4(2023-03-29)
 | 
			
		||||
- 修复 手动上传删除一个文件后不能再上传的bug
 | 
			
		||||
## 1.0.3(2022-12-19)
 | 
			
		||||
- 新增 sourceType 属性, 可以自定义图片和视频选择的来源
 | 
			
		||||
## 1.0.2(2022-07-04)
 | 
			
		||||
- 修复 在uni-forms下样式不生效的bug
 | 
			
		||||
## 1.0.1(2021-11-23)
 | 
			
		||||
- 修复 参数为对象的情况下,url在某些情况显示错误的bug
 | 
			
		||||
## 1.0.0(2021-11-19)
 | 
			
		||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
 | 
			
		||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
 | 
			
		||||
## 0.2.16(2021-11-08)
 | 
			
		||||
- 修复 传入空对象 ,显示错误的Bug
 | 
			
		||||
## 0.2.15(2021-08-30)
 | 
			
		||||
- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug
 | 
			
		||||
## 0.2.14(2021-08-23)
 | 
			
		||||
- 新增 参数中返回 fileID 字段
 | 
			
		||||
## 0.2.13(2021-08-23)
 | 
			
		||||
- 修复 腾讯云传入fileID 不能回显的bug
 | 
			
		||||
- 修复 选择图片后,不能放大的问题
 | 
			
		||||
## 0.2.12(2021-08-17)
 | 
			
		||||
- 修复 由于 0.2.11 版本引起的不能回显图片的Bug
 | 
			
		||||
## 0.2.11(2021-08-16)
 | 
			
		||||
- 新增 clearFiles(index) 方法,可以手动删除指定文件
 | 
			
		||||
- 修复 v-model 值设为 null 报错的Bug
 | 
			
		||||
## 0.2.10(2021-08-13)
 | 
			
		||||
- 修复 return-type="object" 时,无法删除文件的Bug
 | 
			
		||||
## 0.2.9(2021-08-03)
 | 
			
		||||
- 修复 auto-upload 属性失效的Bug
 | 
			
		||||
## 0.2.8(2021-07-31)
 | 
			
		||||
- 修复 fileExtname属性不指定值报错的Bug
 | 
			
		||||
## 0.2.7(2021-07-31)
 | 
			
		||||
- 修复 在某种场景下图片不回显的Bug
 | 
			
		||||
## 0.2.6(2021-07-30)
 | 
			
		||||
- 修复 return-type为object下,返回值不正确的Bug
 | 
			
		||||
## 0.2.5(2021-07-30)
 | 
			
		||||
- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题
 | 
			
		||||
## 0.2.3(2021-07-28)
 | 
			
		||||
- 优化 调整示例代码
 | 
			
		||||
## 0.2.2(2021-07-27)
 | 
			
		||||
- 修复 vue3 下赋值错误的Bug
 | 
			
		||||
- 优化 h5平台下上传文件导致页面卡死的问题
 | 
			
		||||
## 0.2.0(2021-07-13)
 | 
			
		||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
 | 
			
		||||
## 0.1.1(2021-07-02)
 | 
			
		||||
- 修复 sourceType 缺少默认值导致 ios 无法选择文件
 | 
			
		||||
## 0.1.0(2021-06-30)
 | 
			
		||||
- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改
 | 
			
		||||
## 0.0.11(2021-06-30)
 | 
			
		||||
- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题
 | 
			
		||||
## 0.0.10(2021-06-29)
 | 
			
		||||
- 优化 文件上传后进度条消失时机
 | 
			
		||||
## 0.0.9(2021-06-29)
 | 
			
		||||
- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug
 | 
			
		||||
## 0.0.8(2021-06-15)
 | 
			
		||||
- 修复 删除文件时无法触发 v-model 的Bug
 | 
			
		||||
## 0.0.7(2021-05-12)
 | 
			
		||||
- 新增 组件示例地址
 | 
			
		||||
## 0.0.6(2021-04-09)
 | 
			
		||||
- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug
 | 
			
		||||
## 0.0.5(2021-04-09)
 | 
			
		||||
- 优化 更新组件示例
 | 
			
		||||
## 0.0.4(2021-04-09)
 | 
			
		||||
- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔
 | 
			
		||||
## 0.0.3(2021-02-05)
 | 
			
		||||
- 调整为uni_modules目录规范
 | 
			
		||||
- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug
 | 
			
		||||
@@ -0,0 +1,224 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const ERR_MSG_OK = 'chooseAndUploadFile:ok';
 | 
			
		||||
const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
 | 
			
		||||
 | 
			
		||||
function chooseImage(opts) {
 | 
			
		||||
	const {
 | 
			
		||||
		count,
 | 
			
		||||
		sizeType = ['original', 'compressed'],
 | 
			
		||||
		sourceType,
 | 
			
		||||
		extension
 | 
			
		||||
	} = opts
 | 
			
		||||
	return new Promise((resolve, reject) => {
 | 
			
		||||
		uni.chooseImage({
 | 
			
		||||
			count,
 | 
			
		||||
			sizeType,
 | 
			
		||||
			sourceType,
 | 
			
		||||
			extension,
 | 
			
		||||
			success(res) {
 | 
			
		||||
				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
 | 
			
		||||
			},
 | 
			
		||||
			fail(res) {
 | 
			
		||||
				reject({
 | 
			
		||||
					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function chooseVideo(opts) {
 | 
			
		||||
	const {
 | 
			
		||||
		camera,
 | 
			
		||||
		compressed,
 | 
			
		||||
		maxDuration,
 | 
			
		||||
		sourceType,
 | 
			
		||||
		extension
 | 
			
		||||
	} = opts;
 | 
			
		||||
	return new Promise((resolve, reject) => {
 | 
			
		||||
		uni.chooseVideo({
 | 
			
		||||
			camera,
 | 
			
		||||
			compressed,
 | 
			
		||||
			maxDuration,
 | 
			
		||||
			sourceType,
 | 
			
		||||
			extension,
 | 
			
		||||
			success(res) {
 | 
			
		||||
				const {
 | 
			
		||||
					tempFilePath,
 | 
			
		||||
					duration,
 | 
			
		||||
					size,
 | 
			
		||||
					height,
 | 
			
		||||
					width
 | 
			
		||||
				} = res;
 | 
			
		||||
				resolve(normalizeChooseAndUploadFileRes({
 | 
			
		||||
					errMsg: 'chooseVideo:ok',
 | 
			
		||||
					tempFilePaths: [tempFilePath],
 | 
			
		||||
					tempFiles: [
 | 
			
		||||
					{
 | 
			
		||||
						name: (res.tempFile && res.tempFile.name) || '',
 | 
			
		||||
						path: tempFilePath,
 | 
			
		||||
						size,
 | 
			
		||||
						type: (res.tempFile && res.tempFile.type) || '',
 | 
			
		||||
						width,
 | 
			
		||||
						height,
 | 
			
		||||
						duration,
 | 
			
		||||
						fileType: 'video',
 | 
			
		||||
						cloudPath: '',
 | 
			
		||||
					}, ],
 | 
			
		||||
				}, 'video'));
 | 
			
		||||
			},
 | 
			
		||||
			fail(res) {
 | 
			
		||||
				reject({
 | 
			
		||||
					errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function chooseAll(opts) {
 | 
			
		||||
	const {
 | 
			
		||||
		count,
 | 
			
		||||
		extension
 | 
			
		||||
	} = opts;
 | 
			
		||||
	return new Promise((resolve, reject) => {
 | 
			
		||||
		let chooseFile = uni.chooseFile;
 | 
			
		||||
		if (typeof wx !== 'undefined' &&
 | 
			
		||||
			typeof wx.chooseMessageFile === 'function') {
 | 
			
		||||
			chooseFile = wx.chooseMessageFile;
 | 
			
		||||
		}
 | 
			
		||||
		if (typeof chooseFile !== 'function') {
 | 
			
		||||
			return reject({
 | 
			
		||||
				errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		chooseFile({
 | 
			
		||||
			type: 'all',
 | 
			
		||||
			count,
 | 
			
		||||
			extension,
 | 
			
		||||
			success(res) {
 | 
			
		||||
				resolve(normalizeChooseAndUploadFileRes(res));
 | 
			
		||||
			},
 | 
			
		||||
			fail(res) {
 | 
			
		||||
				reject({
 | 
			
		||||
					errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function normalizeChooseAndUploadFileRes(res, fileType) {
 | 
			
		||||
	res.tempFiles.forEach((item, index) => {
 | 
			
		||||
		if (!item.name) {
 | 
			
		||||
			item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
 | 
			
		||||
		}
 | 
			
		||||
		if (fileType) {
 | 
			
		||||
			item.fileType = fileType;
 | 
			
		||||
		}
 | 
			
		||||
		item.cloudPath =
 | 
			
		||||
			Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
 | 
			
		||||
	});
 | 
			
		||||
	if (!res.tempFilePaths) {
 | 
			
		||||
		res.tempFilePaths = res.tempFiles.map((file) => file.path);
 | 
			
		||||
	}
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function uploadCloudFiles(files, max = 5, onUploadProgress) {
 | 
			
		||||
	files = JSON.parse(JSON.stringify(files))
 | 
			
		||||
	const len = files.length
 | 
			
		||||
	let count = 0
 | 
			
		||||
	let self = this
 | 
			
		||||
	return new Promise(resolve => {
 | 
			
		||||
		while (count < max) {
 | 
			
		||||
			next()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		function next() {
 | 
			
		||||
			let cur = count++
 | 
			
		||||
			if (cur >= len) {
 | 
			
		||||
				!files.find(item => !item.url && !item.errMsg) && resolve(files)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			const fileItem = files[cur]
 | 
			
		||||
			const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
 | 
			
		||||
			fileItem.url = ''
 | 
			
		||||
			delete fileItem.errMsg
 | 
			
		||||
 | 
			
		||||
			uniCloud
 | 
			
		||||
				.uploadFile({
 | 
			
		||||
					filePath: fileItem.path,
 | 
			
		||||
					cloudPath: fileItem.cloudPath,
 | 
			
		||||
					fileType: fileItem.fileType,
 | 
			
		||||
					onUploadProgress: res => {
 | 
			
		||||
						res.index = index
 | 
			
		||||
						onUploadProgress && onUploadProgress(res)
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
				.then(res => {
 | 
			
		||||
					fileItem.url = res.fileID
 | 
			
		||||
					fileItem.index = index
 | 
			
		||||
					if (cur < len) {
 | 
			
		||||
						next()
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
				.catch(res => {
 | 
			
		||||
					fileItem.errMsg = res.errMsg || res.message
 | 
			
		||||
					fileItem.index = index
 | 
			
		||||
					if (cur < len) {
 | 
			
		||||
						next()
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function uploadFiles(choosePromise, {
 | 
			
		||||
	onChooseFile,
 | 
			
		||||
	onUploadProgress
 | 
			
		||||
}) {
 | 
			
		||||
	return choosePromise
 | 
			
		||||
		.then((res) => {
 | 
			
		||||
			if (onChooseFile) {
 | 
			
		||||
				const customChooseRes = onChooseFile(res);
 | 
			
		||||
				if (typeof customChooseRes !== 'undefined') {
 | 
			
		||||
					return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ?
 | 
			
		||||
						res : chooseRes);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return res;
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => {
 | 
			
		||||
			if (res === false) {
 | 
			
		||||
				return {
 | 
			
		||||
					errMsg: ERR_MSG_OK,
 | 
			
		||||
					tempFilePaths: [],
 | 
			
		||||
					tempFiles: [],
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
			return res
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function chooseAndUploadFile(opts = {
 | 
			
		||||
	type: 'all'
 | 
			
		||||
}) {
 | 
			
		||||
	if (opts.type === 'image') {
 | 
			
		||||
		return uploadFiles(chooseImage(opts), opts);
 | 
			
		||||
	}
 | 
			
		||||
	else if (opts.type === 'video') {
 | 
			
		||||
		return uploadFiles(chooseVideo(opts), opts);
 | 
			
		||||
	}
 | 
			
		||||
	return uploadFiles(chooseAll(opts), opts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
	chooseAndUploadFile,
 | 
			
		||||
	uploadCloudFiles
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,667 @@
 | 
			
		||||
<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>
 | 
			
		||||
		<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly"
 | 
			
		||||
			:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview"
 | 
			
		||||
			:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
 | 
			
		||||
			<slot>
 | 
			
		||||
				<view class="is-add">
 | 
			
		||||
					<view class="icon-add"></view>
 | 
			
		||||
					<view class="icon-add rotate"></view>
 | 
			
		||||
				</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'
 | 
			
		||||
	let fileInput = null
 | 
			
		||||
	/**
 | 
			
		||||
	 * FilePicker 文件选择上传
 | 
			
		||||
	 * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
 | 
			
		||||
	 * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
 | 
			
		||||
	 * @property {Object|Array}	value	组件数据,通常用来回显 ,类型由return-type属性决定
 | 
			
		||||
	 * @property {Boolean}	disabled = [true|false]	组件禁用
 | 
			
		||||
	 * 	@value true 	禁用
 | 
			
		||||
	 * 	@value false 	取消禁用
 | 
			
		||||
	 * @property {Boolean}	readonly = [true|false]	组件只读,不可选择,不显示进度,不显示删除按钮
 | 
			
		||||
	 * 	@value true 	只读
 | 
			
		||||
	 * 	@value false 	取消只读
 | 
			
		||||
	 * @property {String}	return-type = [array|object]	限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
 | 
			
		||||
	 * 	@value array	规定 value 属性的类型为数组
 | 
			
		||||
	 * 	@value object	规定 value 属性的类型为对象
 | 
			
		||||
	 * @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: 'uniFilePicker',
 | 
			
		||||
		components: {
 | 
			
		||||
			uploadImage,
 | 
			
		||||
			uploadFile
 | 
			
		||||
		},
 | 
			
		||||
		options: {
 | 
			
		||||
			virtualHost: true
 | 
			
		||||
		},
 | 
			
		||||
		emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
 | 
			
		||||
		props: {
 | 
			
		||||
			// #ifdef VUE3
 | 
			
		||||
			modelValue: {
 | 
			
		||||
				type: [Array, Object],
 | 
			
		||||
				default () {
 | 
			
		||||
					return []
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			// #endif
 | 
			
		||||
 | 
			
		||||
			// #ifndef VUE3
 | 
			
		||||
			value: {
 | 
			
		||||
				type: [Array, Object],
 | 
			
		||||
				default () {
 | 
			
		||||
					return []
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			// #endif
 | 
			
		||||
 | 
			
		||||
			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
 | 
			
		||||
			},
 | 
			
		||||
			returnType: {
 | 
			
		||||
				type: String,
 | 
			
		||||
				default: 'array'
 | 
			
		||||
			},
 | 
			
		||||
			sizeType: {
 | 
			
		||||
				type: Array,
 | 
			
		||||
				default () {
 | 
			
		||||
					return ['original', 'compressed']
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			sourceType: {
 | 
			
		||||
				type: Array,
 | 
			
		||||
				default () {
 | 
			
		||||
					return  ['album', 'camera']
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		data() {
 | 
			
		||||
			return {
 | 
			
		||||
				files: [],
 | 
			
		||||
				localValue: []
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		watch: {
 | 
			
		||||
			// #ifndef VUE3
 | 
			
		||||
			value: {
 | 
			
		||||
				handler(newVal, oldVal) {
 | 
			
		||||
					this.setValue(newVal, oldVal)
 | 
			
		||||
				},
 | 
			
		||||
				immediate: true
 | 
			
		||||
			},
 | 
			
		||||
			// #endif
 | 
			
		||||
			// #ifdef VUE3
 | 
			
		||||
			modelValue: {
 | 
			
		||||
				handler(newVal, oldVal) {
 | 
			
		||||
					this.setValue(newVal, oldVal)
 | 
			
		||||
				},
 | 
			
		||||
				immediate: true
 | 
			
		||||
			},
 | 
			
		||||
			// #endif
 | 
			
		||||
		},
 | 
			
		||||
		computed: {
 | 
			
		||||
			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() {
 | 
			
		||||
			// TODO 兼容不开通服务空间的情况
 | 
			
		||||
			if (!(uniCloud.config && uniCloud.config.provider)) {
 | 
			
		||||
				this.noSpace = true
 | 
			
		||||
				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,
 | 
			
		||||
						sourceType: this.sourceType,
 | 
			
		||||
						// 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 || this.noSpace) {
 | 
			
		||||
					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 = []
 | 
			
		||||
				if (this.returnType === 'object') {
 | 
			
		||||
					data = this.backObject(this.files)[0]
 | 
			
		||||
					this.localValue = data?data:null
 | 
			
		||||
				} else {
 | 
			
		||||
					data = this.backObject(this.files)
 | 
			
		||||
					if (!this.localValue) {
 | 
			
		||||
						this.localValue = []
 | 
			
		||||
					}
 | 
			
		||||
					this.localValue = [...data]
 | 
			
		||||
				}
 | 
			
		||||
				// #ifdef VUE3
 | 
			
		||||
				this.$emit('update:modelValue', this.localValue)
 | 
			
		||||
				// #endif
 | 
			
		||||
				// #ifndef VUE3
 | 
			
		||||
				this.$emit('input', this.localValue)
 | 
			
		||||
				// #endif
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 处理返回参数
 | 
			
		||||
			 * @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,
 | 
			
		||||
						// 修改删除一个文件后不能再上传的bug, #694
 | 
			
		||||
            uuid: v.uuid,
 | 
			
		||||
            status: v.status,
 | 
			
		||||
            cloudPath: v.cloudPath
 | 
			
		||||
					})
 | 
			
		||||
				})
 | 
			
		||||
				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>
 | 
			
		||||
	.uni-file-picker {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		overflow: hidden;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		flex: 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-add {
 | 
			
		||||
		width: 50px;
 | 
			
		||||
		height: 5px;
 | 
			
		||||
		background-color: #f1f1f1;
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.rotate {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		transform: rotate(90deg);
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -0,0 +1,325 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view class="uni-file-picker__files">
 | 
			
		||||
		<view v-if="!readonly" class="files-button" @click="choose">
 | 
			
		||||
			<slot></slot>
 | 
			
		||||
		</view>
 | 
			
		||||
		<!-- :class="{'is-text-box':showType === 'list'}" -->
 | 
			
		||||
		<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle">
 | 
			
		||||
			<!-- ,'is-list-card':showType === 'list-card' -->
 | 
			
		||||
 | 
			
		||||
			<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{
 | 
			
		||||
				'files-border':index !== 0 && styles.dividline}"
 | 
			
		||||
			 :style="index !== 0 && styles.dividline &&borderLineStyle">
 | 
			
		||||
				<view class="uni-file-picker__item">
 | 
			
		||||
					<!-- :class="{'is-text-image':showType === 'list'}" -->
 | 
			
		||||
					<!-- 	<view class="files__image is-text-image">
 | 
			
		||||
						<image class="header-image" :src="item.logo" mode="aspectFit"></image>
 | 
			
		||||
					</view> -->
 | 
			
		||||
					<view class="files__name">{{item.name}}</view>
 | 
			
		||||
					<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)">
 | 
			
		||||
						<view class="icon-del icon-files"></view>
 | 
			
		||||
						<view class="icon-del rotate"></view>
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
 | 
			
		||||
					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
 | 
			
		||||
					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
 | 
			
		||||
				</view>
 | 
			
		||||
				<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
 | 
			
		||||
					点击重试
 | 
			
		||||
				</view>
 | 
			
		||||
			</view>
 | 
			
		||||
 | 
			
		||||
		</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
	export default {
 | 
			
		||||
		name: "uploadFile",
 | 
			
		||||
		emits:['uploadFiles','choose','delFile'],
 | 
			
		||||
		props: {
 | 
			
		||||
			filesList: {
 | 
			
		||||
				type: Array,
 | 
			
		||||
				default () {
 | 
			
		||||
					return []
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			delIcon: {
 | 
			
		||||
				type: Boolean,
 | 
			
		||||
				default: true
 | 
			
		||||
			},
 | 
			
		||||
			limit: {
 | 
			
		||||
				type: [Number, String],
 | 
			
		||||
				default: 9
 | 
			
		||||
			},
 | 
			
		||||
			showType: {
 | 
			
		||||
				type: String,
 | 
			
		||||
				default: ''
 | 
			
		||||
			},
 | 
			
		||||
			listStyles: {
 | 
			
		||||
				type: Object,
 | 
			
		||||
				default () {
 | 
			
		||||
					return {
 | 
			
		||||
						// 是否显示边框
 | 
			
		||||
						border: true,
 | 
			
		||||
						// 是否显示分隔线
 | 
			
		||||
						dividline: true,
 | 
			
		||||
						// 线条样式
 | 
			
		||||
						borderStyle: {}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			readonly:{
 | 
			
		||||
				type:Boolean,
 | 
			
		||||
				default:false
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		computed: {
 | 
			
		||||
			list() {
 | 
			
		||||
				let files = []
 | 
			
		||||
				this.filesList.forEach(v => {
 | 
			
		||||
					files.push(v)
 | 
			
		||||
				})
 | 
			
		||||
				return files
 | 
			
		||||
			},
 | 
			
		||||
			styles() {
 | 
			
		||||
				let styles = {
 | 
			
		||||
					border: true,
 | 
			
		||||
					dividline: true,
 | 
			
		||||
					'border-style': {}
 | 
			
		||||
				}
 | 
			
		||||
				return Object.assign(styles, this.listStyles)
 | 
			
		||||
			},
 | 
			
		||||
			borderStyle() {
 | 
			
		||||
				let {
 | 
			
		||||
					borderStyle,
 | 
			
		||||
					border
 | 
			
		||||
				} = this.styles
 | 
			
		||||
				let obj = {}
 | 
			
		||||
				if (!border) {
 | 
			
		||||
					obj.border = 'none'
 | 
			
		||||
				} else {
 | 
			
		||||
					let width = (borderStyle && borderStyle.width) || 1
 | 
			
		||||
					width = this.value2px(width)
 | 
			
		||||
					let radius = (borderStyle && borderStyle.radius) || 5
 | 
			
		||||
					radius = this.value2px(radius)
 | 
			
		||||
					obj = {
 | 
			
		||||
						'border-width': width,
 | 
			
		||||
						'border-style': (borderStyle && borderStyle.style) || 'solid',
 | 
			
		||||
						'border-color': (borderStyle && borderStyle.color) || '#eee',
 | 
			
		||||
						'border-radius': radius
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				let classles = ''
 | 
			
		||||
				for (let i in obj) {
 | 
			
		||||
					classles += `${i}:${obj[i]};`
 | 
			
		||||
				}
 | 
			
		||||
				return classles
 | 
			
		||||
			},
 | 
			
		||||
			borderLineStyle() {
 | 
			
		||||
				let obj = {}
 | 
			
		||||
				let {
 | 
			
		||||
					borderStyle
 | 
			
		||||
				} = this.styles
 | 
			
		||||
				if (borderStyle && borderStyle.color) {
 | 
			
		||||
					obj['border-color'] = borderStyle.color
 | 
			
		||||
				}
 | 
			
		||||
				if (borderStyle && borderStyle.width) {
 | 
			
		||||
					let width = borderStyle && borderStyle.width || 1
 | 
			
		||||
					let style = borderStyle && borderStyle.style || 0
 | 
			
		||||
					if (typeof width === 'number') {
 | 
			
		||||
						width += 'px'
 | 
			
		||||
					} else {
 | 
			
		||||
						width = width.indexOf('px') ? width : width + 'px'
 | 
			
		||||
					}
 | 
			
		||||
					obj['border-width'] = width
 | 
			
		||||
 | 
			
		||||
					if (typeof style === 'number') {
 | 
			
		||||
						style += 'px'
 | 
			
		||||
					} else {
 | 
			
		||||
						style = style.indexOf('px') ? style : style + 'px'
 | 
			
		||||
					}
 | 
			
		||||
					obj['border-top-style'] = style
 | 
			
		||||
				}
 | 
			
		||||
				let classles = ''
 | 
			
		||||
				for (let i in obj) {
 | 
			
		||||
					classles += `${i}:${obj[i]};`
 | 
			
		||||
				}
 | 
			
		||||
				return classles
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		methods: {
 | 
			
		||||
			uploadFiles(item, index) {
 | 
			
		||||
				this.$emit("uploadFiles", {
 | 
			
		||||
					item,
 | 
			
		||||
					index
 | 
			
		||||
				})
 | 
			
		||||
			},
 | 
			
		||||
			choose() {
 | 
			
		||||
				this.$emit("choose")
 | 
			
		||||
			},
 | 
			
		||||
			delFile(index) {
 | 
			
		||||
				this.$emit('delFile', index)
 | 
			
		||||
			},
 | 
			
		||||
			value2px(value) {
 | 
			
		||||
				if (typeof value === 'number') {
 | 
			
		||||
					value += 'px'
 | 
			
		||||
				} else {
 | 
			
		||||
					value = value.indexOf('px') !== -1 ? value : value + 'px'
 | 
			
		||||
				}
 | 
			
		||||
				return value
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
	.uni-file-picker__files {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
		justify-content: flex-start;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.files-button {
 | 
			
		||||
		// border: 1px red solid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.uni-file-picker__lists {
 | 
			
		||||
		position: relative;
 | 
			
		||||
		margin-top: 5px;
 | 
			
		||||
		overflow: hidden;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__mask {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		color: #fff;
 | 
			
		||||
		font-size: 14px;
 | 
			
		||||
		background-color: rgba(0, 0, 0, 0.4);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.uni-file-picker__lists-box {
 | 
			
		||||
		position: relative;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.uni-file-picker__item {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		padding: 8px 10px;
 | 
			
		||||
		padding-right: 5px;
 | 
			
		||||
		padding-left: 10px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.files-border {
 | 
			
		||||
		border-top: 1px #eee solid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.files__name {
 | 
			
		||||
		flex: 1;
 | 
			
		||||
		font-size: 14px;
 | 
			
		||||
		color: #666;
 | 
			
		||||
		margin-right: 25px;
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		word-break: break-all;
 | 
			
		||||
		word-wrap: break-word;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-files {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		position: static;
 | 
			
		||||
		background-color: initial;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// .icon-files .icon-del {
 | 
			
		||||
	// 	background-color: #333;
 | 
			
		||||
	// 	width: 12px;
 | 
			
		||||
	// 	height: 1px;
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	.is-list-card {
 | 
			
		||||
		border: 1px #eee solid;
 | 
			
		||||
		margin-bottom: 5px;
 | 
			
		||||
		border-radius: 5px;
 | 
			
		||||
		box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1);
 | 
			
		||||
		padding: 5px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.files__image {
 | 
			
		||||
		width: 40px;
 | 
			
		||||
		height: 40px;
 | 
			
		||||
		margin-right: 10px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.header-image {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		height: 100%;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-text-box {
 | 
			
		||||
		border: 1px #eee solid;
 | 
			
		||||
		border-radius: 5px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-text-image {
 | 
			
		||||
		width: 25px;
 | 
			
		||||
		height: 25px;
 | 
			
		||||
		margin-left: 5px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.rotate {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		transform: rotate(90deg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-del-box {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		margin: auto 0;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 0px;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		right: 5px;
 | 
			
		||||
		height: 26px;
 | 
			
		||||
		width: 26px;
 | 
			
		||||
		// border-radius: 50%;
 | 
			
		||||
		// background-color: rgba(0, 0, 0, 0.5);
 | 
			
		||||
		z-index: 2;
 | 
			
		||||
		transform: rotate(-45deg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-del {
 | 
			
		||||
		width: 15px;
 | 
			
		||||
		height: 1px;
 | 
			
		||||
		background-color: #333;
 | 
			
		||||
		// border-radius: 1px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* #ifdef H5 */
 | 
			
		||||
	@media all and (min-width: 768px) {
 | 
			
		||||
		.uni-file-picker__files {
 | 
			
		||||
			max-width: 375px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* #endif */
 | 
			
		||||
</style>
 | 
			
		||||
@@ -0,0 +1,292 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view class="uni-file-picker__container">
 | 
			
		||||
		<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
 | 
			
		||||
			<view class="file-picker__box-content" :style="borderStyle">
 | 
			
		||||
				<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
 | 
			
		||||
				<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
 | 
			
		||||
					<view class="icon-del"></view>
 | 
			
		||||
					<view class="icon-del rotate"></view>
 | 
			
		||||
				</view>
 | 
			
		||||
				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
 | 
			
		||||
					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
 | 
			
		||||
					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
 | 
			
		||||
				</view>
 | 
			
		||||
				<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
 | 
			
		||||
					点击重试
 | 
			
		||||
				</view>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
		<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
 | 
			
		||||
			<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
 | 
			
		||||
				<slot>
 | 
			
		||||
					<view class="icon-add"></view>
 | 
			
		||||
					<view class="icon-add rotate"></view>
 | 
			
		||||
				</slot>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
	export default {
 | 
			
		||||
		name: "uploadImage",
 | 
			
		||||
		emits:['uploadFiles','choose','delFile'],
 | 
			
		||||
		props: {
 | 
			
		||||
			filesList: {
 | 
			
		||||
				type: Array,
 | 
			
		||||
				default () {
 | 
			
		||||
					return []
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			disabled:{
 | 
			
		||||
				type: Boolean,
 | 
			
		||||
				default: false
 | 
			
		||||
			},
 | 
			
		||||
			disablePreview: {
 | 
			
		||||
				type: Boolean,
 | 
			
		||||
				default: false
 | 
			
		||||
			},
 | 
			
		||||
			limit: {
 | 
			
		||||
				type: [Number, String],
 | 
			
		||||
				default: 9
 | 
			
		||||
			},
 | 
			
		||||
			imageStyles: {
 | 
			
		||||
				type: Object,
 | 
			
		||||
				default () {
 | 
			
		||||
					return {
 | 
			
		||||
						width: 'auto',
 | 
			
		||||
						height: 'auto',
 | 
			
		||||
						border: {}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			delIcon: {
 | 
			
		||||
				type: Boolean,
 | 
			
		||||
				default: true
 | 
			
		||||
			},
 | 
			
		||||
			readonly:{
 | 
			
		||||
				type:Boolean,
 | 
			
		||||
				default:false
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		computed: {
 | 
			
		||||
			styles() {
 | 
			
		||||
				let styles = {
 | 
			
		||||
					width: 'auto',
 | 
			
		||||
					height: 'auto',
 | 
			
		||||
					border: {}
 | 
			
		||||
				}
 | 
			
		||||
				return Object.assign(styles, this.imageStyles)
 | 
			
		||||
			},
 | 
			
		||||
			boxStyle() {
 | 
			
		||||
				const {
 | 
			
		||||
					width = 'auto',
 | 
			
		||||
						height = 'auto'
 | 
			
		||||
				} = this.styles
 | 
			
		||||
				let obj = {}
 | 
			
		||||
				if (height === 'auto') {
 | 
			
		||||
					if (width !== 'auto') {
 | 
			
		||||
						obj.height = this.value2px(width)
 | 
			
		||||
						obj['padding-top'] = 0
 | 
			
		||||
					} else {
 | 
			
		||||
						obj.height = 0
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					obj.height = this.value2px(height)
 | 
			
		||||
					obj['padding-top'] = 0
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (width === 'auto') {
 | 
			
		||||
					if (height !== 'auto') {
 | 
			
		||||
						obj.width = this.value2px(height)
 | 
			
		||||
					} else {
 | 
			
		||||
						obj.width = '33.3%'
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					obj.width = this.value2px(width)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				let classles = ''
 | 
			
		||||
				for(let i in obj){
 | 
			
		||||
					classles+= `${i}:${obj[i]};`
 | 
			
		||||
				}
 | 
			
		||||
				return classles
 | 
			
		||||
			},
 | 
			
		||||
			borderStyle() {
 | 
			
		||||
				let {
 | 
			
		||||
					border
 | 
			
		||||
				} = this.styles
 | 
			
		||||
				let obj = {}
 | 
			
		||||
				const widthDefaultValue = 1
 | 
			
		||||
				const radiusDefaultValue = 3
 | 
			
		||||
				if (typeof border === 'boolean') {
 | 
			
		||||
					obj.border = border ? '1px #eee solid' : 'none'
 | 
			
		||||
				} else {
 | 
			
		||||
					let width = (border && border.width) || widthDefaultValue
 | 
			
		||||
					width = this.value2px(width)
 | 
			
		||||
					let radius = (border && border.radius) || radiusDefaultValue
 | 
			
		||||
					radius = this.value2px(radius)
 | 
			
		||||
					obj = {
 | 
			
		||||
						'border-width': width,
 | 
			
		||||
						'border-style': (border && border.style) || 'solid',
 | 
			
		||||
						'border-color': (border && border.color) || '#eee',
 | 
			
		||||
						'border-radius': radius
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				let classles = ''
 | 
			
		||||
				for(let i in obj){
 | 
			
		||||
					classles+= `${i}:${obj[i]};`
 | 
			
		||||
				}
 | 
			
		||||
				return classles
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		methods: {
 | 
			
		||||
			uploadFiles(item, index) {
 | 
			
		||||
				this.$emit("uploadFiles", item)
 | 
			
		||||
			},
 | 
			
		||||
			choose() {
 | 
			
		||||
				this.$emit("choose")
 | 
			
		||||
			},
 | 
			
		||||
			delFile(index) {
 | 
			
		||||
				this.$emit('delFile', index)
 | 
			
		||||
			},
 | 
			
		||||
			prviewImage(img, index) {
 | 
			
		||||
				let urls = []
 | 
			
		||||
				if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
 | 
			
		||||
					this.$emit("choose")
 | 
			
		||||
				}
 | 
			
		||||
				if(this.disablePreview) return
 | 
			
		||||
				this.filesList.forEach(i => {
 | 
			
		||||
					urls.push(i.url)
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
				uni.previewImage({
 | 
			
		||||
					urls: urls,
 | 
			
		||||
					current: index
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
			value2px(value) {
 | 
			
		||||
				if (typeof value === 'number') {
 | 
			
		||||
					value += 'px'
 | 
			
		||||
				} else {
 | 
			
		||||
					if (value.indexOf('%') === -1) {
 | 
			
		||||
						value = value.indexOf('px') !== -1 ? value : value + 'px'
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return value
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
	.uni-file-picker__container {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		flex-wrap: wrap;
 | 
			
		||||
		margin: -5px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__box {
 | 
			
		||||
		position: relative;
 | 
			
		||||
		// flex: 0 0 33.3%;
 | 
			
		||||
		width: 33.3%;
 | 
			
		||||
		height: 0;
 | 
			
		||||
		padding-top: 33.33%;
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__box-content {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		margin: 5px;
 | 
			
		||||
		border: 1px #eee solid;
 | 
			
		||||
		border-radius: 5px;
 | 
			
		||||
		overflow: hidden;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__progress {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		/* border: 1px red solid; */
 | 
			
		||||
		z-index: 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__progress-item {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-picker__mask {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		color: #fff;
 | 
			
		||||
		font-size: 12px;
 | 
			
		||||
		background-color: rgba(0, 0, 0, 0.4);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.file-image {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		height: 100%;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-add {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-add {
 | 
			
		||||
		width: 50px;
 | 
			
		||||
		height: 5px;
 | 
			
		||||
		background-color: #f1f1f1;
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.rotate {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		transform: rotate(90deg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-del-box {
 | 
			
		||||
		/* #ifndef APP-NVUE */
 | 
			
		||||
		display: flex;
 | 
			
		||||
		/* #endif */
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 3px;
 | 
			
		||||
		right: 3px;
 | 
			
		||||
		height: 26px;
 | 
			
		||||
		width: 26px;
 | 
			
		||||
		border-radius: 50%;
 | 
			
		||||
		background-color: rgba(0, 0, 0, 0.5);
 | 
			
		||||
		z-index: 2;
 | 
			
		||||
		transform: rotate(-45deg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon-del {
 | 
			
		||||
		width: 15px;
 | 
			
		||||
		height: 2px;
 | 
			
		||||
		background-color: #fff;
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										109
									
								
								uni_modules/uni-file-picker/components/uni-file-picker/utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								uni_modules/uni-file-picker/components/uni-file-picker/utils.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 获取文件名和后缀
 | 
			
		||||
 * @param {String} name
 | 
			
		||||
 */
 | 
			
		||||
export const get_file_ext = (name) => {
 | 
			
		||||
	const last_len = name.lastIndexOf('.')
 | 
			
		||||
	const len = name.length
 | 
			
		||||
	return {
 | 
			
		||||
		name: name.substring(0, last_len),
 | 
			
		||||
		ext: name.substring(last_len + 1, len)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取扩展名
 | 
			
		||||
 * @param {Array} fileExtname
 | 
			
		||||
 */
 | 
			
		||||
export const get_extname = (fileExtname) => {
 | 
			
		||||
	if (!Array.isArray(fileExtname)) {
 | 
			
		||||
		let extname = fileExtname.replace(/(\[|\])/g, '')
 | 
			
		||||
		return extname.split(',')
 | 
			
		||||
	} else {
 | 
			
		||||
		return fileExtname
 | 
			
		||||
	}
 | 
			
		||||
	return []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取文件和检测是否可选
 | 
			
		||||
 */
 | 
			
		||||
export const get_files_and_is_max = (res, _extname) => {
 | 
			
		||||
	let filePaths = []
 | 
			
		||||
	let files = []
 | 
			
		||||
	if(!_extname || _extname.length === 0){
 | 
			
		||||
		return {
 | 
			
		||||
			filePaths,
 | 
			
		||||
			files
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	res.tempFiles.forEach(v => {
 | 
			
		||||
		let fileFullName = get_file_ext(v.name)
 | 
			
		||||
		const extname = fileFullName.ext.toLowerCase()
 | 
			
		||||
		if (_extname.indexOf(extname) !== -1) {
 | 
			
		||||
			files.push(v)
 | 
			
		||||
			filePaths.push(v.path)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	if (files.length !== res.tempFiles.length) {
 | 
			
		||||
		uni.showToast({
 | 
			
		||||
			title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`,
 | 
			
		||||
			icon: 'none',
 | 
			
		||||
			duration: 5000
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		filePaths,
 | 
			
		||||
		files
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取图片信息
 | 
			
		||||
 * @param {Object} filepath
 | 
			
		||||
 */
 | 
			
		||||
export const get_file_info = (filepath) => {
 | 
			
		||||
	return new Promise((resolve, reject) => {
 | 
			
		||||
		uni.getImageInfo({
 | 
			
		||||
			src: filepath,
 | 
			
		||||
			success(res) {
 | 
			
		||||
				resolve(res)
 | 
			
		||||
			},
 | 
			
		||||
			fail(err) {
 | 
			
		||||
				reject(err)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 获取封装数据
 | 
			
		||||
 */
 | 
			
		||||
export const get_file_data = async (files, type = 'image') => {
 | 
			
		||||
	// 最终需要上传数据库的数据
 | 
			
		||||
	let fileFullName = get_file_ext(files.name)
 | 
			
		||||
	const extname = fileFullName.ext.toLowerCase()
 | 
			
		||||
	let filedata = {
 | 
			
		||||
		name: files.name,
 | 
			
		||||
		uuid: files.uuid,
 | 
			
		||||
		extname: extname || '',
 | 
			
		||||
		cloudPath: files.cloudPath,
 | 
			
		||||
		fileType: files.fileType,
 | 
			
		||||
		url: files.path || files.path,
 | 
			
		||||
		size: files.size, //单位是字节
 | 
			
		||||
		image: {},
 | 
			
		||||
		path: files.path,
 | 
			
		||||
		video: {}
 | 
			
		||||
	}
 | 
			
		||||
	if (type === 'image') {
 | 
			
		||||
		const imageinfo = await get_file_info(files.path)
 | 
			
		||||
		delete filedata.video
 | 
			
		||||
		filedata.image.width = imageinfo.width
 | 
			
		||||
		filedata.image.height = imageinfo.height
 | 
			
		||||
		filedata.image.location = imageinfo.path
 | 
			
		||||
	} else {
 | 
			
		||||
		delete filedata.image
 | 
			
		||||
	}
 | 
			
		||||
	return filedata
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								uni_modules/uni-file-picker/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								uni_modules/uni-file-picker/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
{
 | 
			
		||||
  "id": "uni-file-picker",
 | 
			
		||||
  "displayName": "uni-file-picker 文件选择上传",
 | 
			
		||||
  "version": "1.0.4",
 | 
			
		||||
  "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "uni-ui",
 | 
			
		||||
    "uniui",
 | 
			
		||||
    "图片上传",
 | 
			
		||||
    "文件上传"
 | 
			
		||||
],
 | 
			
		||||
  "repository": "https://github.com/dcloudio/uni-ui",
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "HBuilderX": ""
 | 
			
		||||
  },
 | 
			
		||||
  "directories": {
 | 
			
		||||
    "example": "../../temps/example_temps"
 | 
			
		||||
  },
 | 
			
		||||
"dcloudext": {
 | 
			
		||||
    "sale": {
 | 
			
		||||
      "regular": {
 | 
			
		||||
        "price": "0.00"
 | 
			
		||||
      },
 | 
			
		||||
      "sourcecode": {
 | 
			
		||||
        "price": "0.00"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "contact": {
 | 
			
		||||
      "qq": ""
 | 
			
		||||
    },
 | 
			
		||||
    "declaration": {
 | 
			
		||||
      "ads": "无",
 | 
			
		||||
      "data": "无",
 | 
			
		||||
      "permissions": "无"
 | 
			
		||||
    },
 | 
			
		||||
    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
 | 
			
		||||
    "type": "component-vue"
 | 
			
		||||
  },
 | 
			
		||||
  "uni_modules": {
 | 
			
		||||
    "dependencies": ["uni-scss"],
 | 
			
		||||
    "encrypt": [],
 | 
			
		||||
    "platforms": {
 | 
			
		||||
      "cloud": {
 | 
			
		||||
        "tcb": "y",
 | 
			
		||||
        "aliyun": "y"
 | 
			
		||||
      },
 | 
			
		||||
      "client": {
 | 
			
		||||
        "App": {
 | 
			
		||||
          "app-vue": "y",
 | 
			
		||||
          "app-nvue": "n"
 | 
			
		||||
        },
 | 
			
		||||
        "H5-mobile": {
 | 
			
		||||
          "Safari": "y",
 | 
			
		||||
          "Android Browser": "y",
 | 
			
		||||
          "微信浏览器(Android)": "y",
 | 
			
		||||
          "QQ浏览器(Android)": "y"
 | 
			
		||||
        },
 | 
			
		||||
        "H5-pc": {
 | 
			
		||||
          "Chrome": "y",
 | 
			
		||||
          "IE": "y",
 | 
			
		||||
          "Edge": "y",
 | 
			
		||||
          "Firefox": "y",
 | 
			
		||||
          "Safari": "y"
 | 
			
		||||
        },
 | 
			
		||||
        "小程序": {
 | 
			
		||||
          "微信": "y",
 | 
			
		||||
          "阿里": "y",
 | 
			
		||||
          "百度": "y",
 | 
			
		||||
          "字节跳动": "y",
 | 
			
		||||
          "QQ": "y"
 | 
			
		||||
        },
 | 
			
		||||
        "快应用": {
 | 
			
		||||
          "华为": "u",
 | 
			
		||||
          "联盟": "u"
 | 
			
		||||
        },
 | 
			
		||||
        "Vue": {
 | 
			
		||||
            "vue2": "y",
 | 
			
		||||
            "vue3": "y"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								uni_modules/uni-file-picker/readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								uni_modules/uni-file-picker/readme.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
 | 
			
		||||
## FilePicker 文件选择上传
 | 
			
		||||
 | 
			
		||||
> **组件名:uni-file-picker**
 | 
			
		||||
>  代码块: `uFilePicker`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
 | 
			
		||||
 | 
			
		||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
 | 
			
		||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
 | 
			
		||||
		Reference in New Issue
	
	Block a user