358 lines
9.6 KiB
JavaScript
358 lines
9.6 KiB
JavaScript
|
|
// 媒体选择工具类 - 支持拍照、拍视频、相册选择
|
|||
|
|
class MediaPicker {
|
|||
|
|
constructor() {
|
|||
|
|
this.maxImageCount = 9; // 最多选择9张图片
|
|||
|
|
this.maxVideoCount = 1; // 最多选择1个视频
|
|||
|
|
this.maxVideoDuration = 60; // 视频最长60秒
|
|||
|
|
this.maxImageSize = 10 * 1024 * 1024; // 图片最大10MB
|
|||
|
|
this.maxVideoSize = 100 * 1024 * 1024; // 视频最大100MB
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔥 ===== 图片选择功能 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 选择图片 - 支持拍照和相册
|
|||
|
|
* @param {Object} options 选项
|
|||
|
|
* @param {number} options.count 最多选择数量,默认9
|
|||
|
|
* @param {Array} options.sourceType 来源类型 ['album', 'camera']
|
|||
|
|
* @param {Array} options.sizeType 图片尺寸 ['original', 'compressed']
|
|||
|
|
* @returns {Promise} 返回选择的图片信息
|
|||
|
|
*/
|
|||
|
|
async chooseImages(options = {}) {
|
|||
|
|
const {
|
|||
|
|
count = this.maxImageCount,
|
|||
|
|
sourceType = ['album', 'camera'],
|
|||
|
|
sizeType = ['compressed', 'original']
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 优先使用新版API
|
|||
|
|
if (wx.chooseMedia) {
|
|||
|
|
return await this.chooseMediaImages({ count, sourceType, sizeType });
|
|||
|
|
} else {
|
|||
|
|
return await this.chooseImageLegacy({ count, sourceType, sizeType });
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ 选择图片失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用新版chooseMedia API选择图片
|
|||
|
|
*/
|
|||
|
|
chooseMediaImages(options) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: options.count,
|
|||
|
|
mediaType: ['image'],
|
|||
|
|
sourceType: options.sourceType,
|
|||
|
|
camera: 'back',
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
const images = res.tempFiles.map(file => ({
|
|||
|
|
path: file.tempFilePath,
|
|||
|
|
size: file.size,
|
|||
|
|
type: 'image',
|
|||
|
|
width: file.width || 0,
|
|||
|
|
height: file.height || 0,
|
|||
|
|
duration: 0
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
files: images,
|
|||
|
|
count: images.length
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
console.error('❌ chooseMedia选择图片失败:', error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用传统chooseImage API选择图片
|
|||
|
|
*/
|
|||
|
|
chooseImageLegacy(options) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseImage({
|
|||
|
|
count: options.count,
|
|||
|
|
sizeType: options.sizeType,
|
|||
|
|
sourceType: options.sourceType,
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
const images = res.tempFilePaths.map((path, index) => ({
|
|||
|
|
path: path,
|
|||
|
|
size: res.tempFiles ? res.tempFiles[index]?.size || 0 : 0,
|
|||
|
|
type: 'image',
|
|||
|
|
width: 0,
|
|||
|
|
height: 0,
|
|||
|
|
duration: 0
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
files: images,
|
|||
|
|
count: images.length
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
console.error('❌ chooseImage选择图片失败:', error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔥 ===== 视频选择功能 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 选择视频 - 支持拍摄和相册
|
|||
|
|
* @param {Object} options 选项
|
|||
|
|
* @param {Array} options.sourceType 来源类型 ['album', 'camera']
|
|||
|
|
* @param {number} options.maxDuration 最大时长(秒)
|
|||
|
|
* @param {string} options.camera 摄像头 'front'|'back'
|
|||
|
|
* @returns {Promise} 返回选择的视频信息
|
|||
|
|
*/
|
|||
|
|
async chooseVideo(options = {}) {
|
|||
|
|
const {
|
|||
|
|
sourceType = ['album', 'camera'],
|
|||
|
|
maxDuration = this.maxVideoDuration,
|
|||
|
|
camera = 'back'
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 优先使用新版API
|
|||
|
|
if (wx.chooseMedia) {
|
|||
|
|
return await this.chooseMediaVideo({ sourceType, maxDuration, camera });
|
|||
|
|
} else {
|
|||
|
|
return await this.chooseVideoLegacy({ sourceType, maxDuration, camera });
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ 选择视频失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用新版chooseMedia API选择视频
|
|||
|
|
*/
|
|||
|
|
chooseMediaVideo(options) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: 1,
|
|||
|
|
mediaType: ['video'],
|
|||
|
|
sourceType: options.sourceType,
|
|||
|
|
maxDuration: options.maxDuration,
|
|||
|
|
camera: options.camera,
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
// 健壮性判断,防止tempFiles为空或无元素
|
|||
|
|
if (!res.tempFiles || !Array.isArray(res.tempFiles) || res.tempFiles.length === 0) {
|
|||
|
|
console.warn('⚠️ chooseMedia返回空tempFiles:', res);
|
|||
|
|
resolve({ success: false, files: [], count: 0, message: '未选择视频' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const video = res.tempFiles[0];
|
|||
|
|
if (!video || !video.tempFilePath) {
|
|||
|
|
console.warn('⚠️ chooseMedia返回的video对象异常:', video);
|
|||
|
|
resolve({ success: false, files: [], count: 0, message: '未选择有效视频' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const videoInfo = {
|
|||
|
|
path: video.tempFilePath,
|
|||
|
|
size: video.size,
|
|||
|
|
type: 'video',
|
|||
|
|
width: video.width || 0,
|
|||
|
|
height: video.height || 0,
|
|||
|
|
duration: video.duration || 0,
|
|||
|
|
thumbTempFilePath: video.thumbTempFilePath || ''
|
|||
|
|
};
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
files: [videoInfo],
|
|||
|
|
count: 1
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
console.error('❌ chooseMedia选择视频失败:', error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用传统chooseVideo API选择视频
|
|||
|
|
*/
|
|||
|
|
chooseVideoLegacy(options) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseVideo({
|
|||
|
|
sourceType: options.sourceType,
|
|||
|
|
maxDuration: options.maxDuration,
|
|||
|
|
camera: options.camera,
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
const videoInfo = {
|
|||
|
|
path: res.tempFilePath,
|
|||
|
|
size: res.size || 0,
|
|||
|
|
type: 'video',
|
|||
|
|
width: res.width || 0,
|
|||
|
|
height: res.height || 0,
|
|||
|
|
duration: res.duration || 0,
|
|||
|
|
thumbTempFilePath: res.thumbTempFilePath || ''
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
files: [videoInfo],
|
|||
|
|
count: 1
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
console.error('❌ chooseVideo选择视频失败:', error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔥 ===== 混合媒体选择功能 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 选择混合媒体 - 图片和视频
|
|||
|
|
* @param {Object} options 选项
|
|||
|
|
* @returns {Promise} 返回选择的媒体信息
|
|||
|
|
*/
|
|||
|
|
async chooseMedia(options = {}) {
|
|||
|
|
const {
|
|||
|
|
count = 9,
|
|||
|
|
mediaType = ['image', 'video'],
|
|||
|
|
sourceType = ['album', 'camera'],
|
|||
|
|
maxDuration = this.maxVideoDuration,
|
|||
|
|
camera = 'back'
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!wx.chooseMedia) {
|
|||
|
|
throw new Error('当前微信版本不支持chooseMedia API');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: count,
|
|||
|
|
mediaType: mediaType,
|
|||
|
|
sourceType: sourceType,
|
|||
|
|
maxDuration: maxDuration,
|
|||
|
|
camera: camera,
|
|||
|
|
success: (res) => {
|
|||
|
|
|
|||
|
|
const files = res.tempFiles.map(file => ({
|
|||
|
|
path: file.tempFilePath,
|
|||
|
|
size: file.size,
|
|||
|
|
type: file.fileType, // 'image' 或 'video'
|
|||
|
|
width: file.width || 0,
|
|||
|
|
height: file.height || 0,
|
|||
|
|
duration: file.duration || 0,
|
|||
|
|
thumbTempFilePath: file.thumbTempFilePath || ''
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
files: files,
|
|||
|
|
count: files.length
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
console.error('❌ chooseMedia选择媒体失败:', error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔥 ===== 工具方法 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查文件大小
|
|||
|
|
*/
|
|||
|
|
checkFileSize(file) {
|
|||
|
|
const maxSize = file.type === 'image' ? this.maxImageSize : this.maxVideoSize;
|
|||
|
|
if (file.size > maxSize) {
|
|||
|
|
const maxSizeMB = Math.round(maxSize / 1024 / 1024);
|
|||
|
|
throw new Error(`文件大小超过限制,最大支持${maxSizeMB}MB`);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取文件信息
|
|||
|
|
*/
|
|||
|
|
async getFileInfo(filePath) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.getFileInfo({
|
|||
|
|
filePath: filePath,
|
|||
|
|
success: resolve,
|
|||
|
|
fail: reject
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示选择媒体的操作菜单
|
|||
|
|
*/
|
|||
|
|
showMediaActionSheet(options = {}) {
|
|||
|
|
const {
|
|||
|
|
showCamera = true,
|
|||
|
|
showAlbum = true,
|
|||
|
|
showVideo = true
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
const itemList = [];
|
|||
|
|
const actions = [];
|
|||
|
|
|
|||
|
|
if (showCamera) {
|
|||
|
|
itemList.push('拍照');
|
|||
|
|
actions.push(() => this.chooseImages({ sourceType: ['camera'] }));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (showAlbum) {
|
|||
|
|
itemList.push('从相册选择图片');
|
|||
|
|
actions.push(() => this.chooseImages({ sourceType: ['album'] }));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (showVideo) {
|
|||
|
|
itemList.push('拍摄视频');
|
|||
|
|
actions.push(() => this.chooseVideo({ sourceType: ['camera'] }));
|
|||
|
|
|
|||
|
|
itemList.push('从相册选择视频');
|
|||
|
|
actions.push(() => this.chooseVideo({ sourceType: ['album'] }));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.showActionSheet({
|
|||
|
|
itemList: itemList,
|
|||
|
|
success: (res) => {
|
|||
|
|
const selectedAction = actions[res.tapIndex];
|
|||
|
|
if (selectedAction) {
|
|||
|
|
selectedAction().then(resolve).catch(reject);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
if (error.errMsg !== 'showActionSheet:fail cancel') {
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建全局单例
|
|||
|
|
const mediaPicker = new MediaPicker();
|
|||
|
|
|
|||
|
|
module.exports = mediaPicker;
|
|||
|
|
|