SmartParks_uniapp/utils/mediaSelector.js

328 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 媒体选择器工具类
* 用于选择本地图片和视频
* 支持设置选择类型(图片、视频或两者)和选择数量
*/
// 媒体类型枚举
export const MediaType = {
IMAGE: 'image',
VIDEO: 'video',
BOTH: 'both'
};
/**
* 媒体选择器类
*/
export default class MediaSelector {
/**
* 选择媒体文件(图片或视频)
* @param {Object} options 选择配置项
* @param {string} options.type 媒体类型,可选值:'image'、'video'、'both',默认为'image'
* @param {number} options.count 最大选择数量默认为9
* @param {Array<string>} options.imageExtensions 图片扩展名,默认为['png', 'jpg', 'jpeg', 'gif', 'webp']
* @param {Array<string>} options.videoExtensions 视频扩展名,默认为['mp4', 'mov', '3gp', 'avi', 'rmvb', 'rm', 'flv', 'mkv']
* @param {boolean} options.compressed 是否压缩所选文件默认为true
* @param {boolean} options.crop 是否裁剪仅对图片有效默认为false
* @param {number} options.videoMaxDuration 拍摄视频最长拍摄时间单位秒默认为60
* @param {string} options.camera 使用的摄像头,可选值:'back'、'front',默认为'back'
* @returns {Promise<Array>} 返回选择的媒体文件数组
*/
static async choose(options = {}) {
const {
type = MediaType.IMAGE,
count = 9,
imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'webp'],
videoExtensions = ['mp4', 'mov', '3gp', 'avi', 'rmvb', 'rm', 'flv', 'mkv'],
compressed = true,
crop = false,
videoMaxDuration = 60,
camera = 'back'
} = options;
// 根据类型选择不同的媒体
if (type === MediaType.IMAGE) {
return this.chooseImages({ count, extensions: imageExtensions, compressed, crop, camera });
} else if (type === MediaType.VIDEO) {
return this.chooseVideos({ count, extensions: videoExtensions, compressed, maxDuration: videoMaxDuration, camera });
} else if (type === MediaType.BOTH) {
// 如果是两者都选,则先让用户选择类型
return new Promise((resolve, reject) => {
uni.showActionSheet({
itemList: ['选择图片', '选择视频'],
success: async (res) => {
try {
if (res.tapIndex === 0) {
// 选择图片
const images = await this.chooseImages({ count, extensions: imageExtensions, compressed, crop, camera });
resolve(images);
} else {
// 选择视频
const videos = await this.chooseVideos({ count, extensions: videoExtensions, compressed, maxDuration: videoMaxDuration, camera });
resolve(videos);
}
} catch (error) {
reject(error);
}
},
fail: (err) => {
reject(err);
}
});
});
} else {
throw new Error('不支持的媒体类型');
}
}
/**
* 选择图片
* @param {Object} options 选择图片的配置项
* @returns {Promise<Array>} 返回选择的图片数组
*/
static chooseImages(options) {
const { count = 9, extensions = ['png', 'jpg', 'jpeg', 'gif', 'webp'], compressed = true, crop = false, camera = 'back' } = options;
return new Promise((resolve, reject) => {
uni.chooseImage({
count,
sizeType: compressed ? ['compressed'] : ['original'],
sourceType: ['album', 'camera'],
extension: extensions,
crop: crop ? {
quality: 100,
width: 300,
height: 300,
resize: true
} : false,
camera,
success: (res) => {
// 处理返回的图片数据,统一格式
const images = res.tempFiles.map(file => ({
path: file.path,
size: file.size,
name: this.getFileName(file.path),
type: 'image',
extension: this.getFileExtension(file.path),
createTime: new Date().getTime()
}));
resolve(images);
},
fail: (err) => {
// 用户取消选择不报错
if (err.errMsg.indexOf('cancel') !== -1) {
resolve([]);
} else {
reject(err);
}
}
});
});
}
/**
* 选择视频
* @param {Object} options 选择视频的配置项
* @returns {Promise<Array>} 返回选择的视频数组
*/
static chooseVideos(options) {
const { count = 1, extensions = ['mp4', 'mov', '3gp', 'avi', 'rmvb', 'rm', 'flv', 'mkv'], compressed = true, maxDuration = 60, camera = 'back' } = options;
// 由于uni.chooseVideo一次只能选择一个视频如果count>1需要多次选择
if (count <= 1) {
return this.chooseSingleVideo({ extensions, compressed, maxDuration, camera });
} else {
// 提示用户需要多次选择
return new Promise((resolve, reject) => {
uni.showModal({
title: '提示',
content: `您需要选择${count}个视频,将分${count}次选择`,
confirmText: '开始选择',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
try {
const videos = [];
for (let i = 0; i < count; i++) {
// 显示当前选择进度
uni.showLoading({
title: `正在选择第${i + 1}/${count}个视频`,
mask: true
});
// 选择单个视频
const videoResult = await this.chooseSingleVideo({ extensions, compressed, maxDuration, camera });
// 隐藏加载提示
uni.hideLoading();
// 如果用户取消了选择,则结束循环
if (videoResult.length === 0) {
break;
}
// 添加到结果数组
videos.push(...videoResult);
// 如果还没选完,询问是否继续
if (i < count - 1) {
const continueRes = await new Promise((resolveDialog) => {
uni.showModal({
title: '提示',
content: `已选择${videos.length}个视频,是否继续选择?`,
confirmText: '继续',
cancelText: '完成',
success: (modalRes) => {
resolveDialog(modalRes.confirm);
}
});
});
// 如果用户选择不继续,则结束循环
if (!continueRes) {
break;
}
}
}
resolve(videos);
} catch (error) {
uni.hideLoading();
reject(error);
}
} else {
resolve([]);
}
},
fail: (err) => {
reject(err);
}
});
});
}
}
/**
* 选择单个视频
* @param {Object} options 选择视频的配置项
* @returns {Promise<Array>} 返回选择的视频数组
*/
static chooseSingleVideo(options) {
const { extensions = ['mp4', 'mov', '3gp', 'avi', 'rmvb', 'rm', 'flv', 'mkv'], compressed = true, maxDuration = 60, camera = 'back' } = options;
return new Promise((resolve, reject) => {
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed,
maxDuration,
camera,
extension: extensions,
success: (res) => {
// 处理返回的视频数据,统一格式
const video = {
path: res.tempFilePath,
size: res.size,
duration: res.duration,
width: res.width,
height: res.height,
name: this.getFileName(res.tempFilePath),
type: 'video',
extension: this.getFileExtension(res.tempFilePath),
createTime: new Date().getTime()
};
resolve([video]);
},
fail: (err) => {
// 用户取消选择不报错
if (err.errMsg.indexOf('cancel') !== -1) {
resolve([]);
} else {
reject(err);
}
}
});
});
}
/**
* 获取文件名
* @param {string} path 文件路径
* @returns {string} 文件名
*/
static getFileName(path) {
if (!path) return '';
return path.substring(path.lastIndexOf('/') + 1);
}
/**
* 获取文件扩展名
* @param {string} path 文件路径
* @returns {string} 文件扩展名
*/
static getFileExtension(path) {
if (!path) return '';
return path.substring(path.lastIndexOf('.') + 1).toLowerCase();
}
/**
* 预览媒体文件
* @param {string} path 文件路径
* @param {string} type 媒体类型,可选值:'image'、'video',默认根据文件扩展名自动判断
*/
static preview(path, type) {
if (!path) return;
// 如果未指定类型,则根据文件扩展名判断
if (!type) {
const extension = this.getFileExtension(path);
if (['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(extension)) {
type = MediaType.IMAGE;
} else if (['mp4', 'mov', '3gp', 'avi', 'rmvb', 'rm', 'flv', 'mkv'].includes(extension)) {
type = MediaType.VIDEO;
} else {
uni.showToast({
title: '不支持的文件类型',
icon: 'none'
});
return;
}
}
// 根据类型预览
if (type === MediaType.IMAGE) {
uni.previewImage({
urls: [path],
current: path
});
} else if (type === MediaType.VIDEO) {
// 视频预览
uni.navigateTo({
url: `/pages/common/video-player?url=${encodeURIComponent(path)}`
});
}
}
}
/**
* 使用示例:
*
* // 导入
* import MediaSelector, { MediaType } from '@/utils/mediaSelector';
*
* // 选择图片
* const images = await MediaSelector.choose({ type: MediaType.IMAGE, count: 9 });
* console.log('选择的图片:', images);
*
* // 选择视频
* const videos = await MediaSelector.choose({ type: MediaType.VIDEO, count: 1 });
* console.log('选择的视频:', videos);
*
* // 选择图片或视频(用户可选择)
* const media = await MediaSelector.choose({ type: MediaType.BOTH, count: 5 });
* console.log('选择的媒体:', media);
*
* // 预览媒体
* if (media.length > 0) {
* MediaSelector.preview(media[0].path, media[0].type);
* }
*/