556 lines
12 KiB
JavaScript
556 lines
12 KiB
JavaScript
|
|
// 🎨 媒体预览组件逻辑
|
||
|
|
const mediaManager = require('../../utils/media-manager.js');
|
||
|
|
|
||
|
|
Component({
|
||
|
|
properties: {
|
||
|
|
// 是否显示预览
|
||
|
|
visible: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
},
|
||
|
|
|
||
|
|
// 媒体列表
|
||
|
|
mediaList: {
|
||
|
|
type: Array,
|
||
|
|
value: []
|
||
|
|
},
|
||
|
|
|
||
|
|
// 当前索引
|
||
|
|
currentIndex: {
|
||
|
|
type: Number,
|
||
|
|
value: 0
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否可以分享
|
||
|
|
canShare: {
|
||
|
|
type: Boolean,
|
||
|
|
value: true
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否可以编辑
|
||
|
|
canEdit: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否可以删除
|
||
|
|
canDelete: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否显示底部操作栏
|
||
|
|
showFooter: {
|
||
|
|
type: Boolean,
|
||
|
|
value: true
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否显示手势提示
|
||
|
|
showGestureTips: {
|
||
|
|
type: Boolean,
|
||
|
|
value: true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
data: {
|
||
|
|
// 当前媒体
|
||
|
|
currentMedia: {},
|
||
|
|
|
||
|
|
// 音频播放状态
|
||
|
|
audioPlaying: false,
|
||
|
|
audioProgress: 0,
|
||
|
|
audioCurrentTime: 0,
|
||
|
|
|
||
|
|
// 手势提示定时器
|
||
|
|
gestureTimer: null
|
||
|
|
},
|
||
|
|
|
||
|
|
observers: {
|
||
|
|
'mediaList, currentIndex': function(mediaList, currentIndex) {
|
||
|
|
if (mediaList && mediaList.length > 0 && currentIndex >= 0 && currentIndex < mediaList.length) {
|
||
|
|
this.setData({
|
||
|
|
currentMedia: mediaList[currentIndex]
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
lifetimes: {
|
||
|
|
attached() {
|
||
|
|
console.log('🎨 媒体预览组件已加载');
|
||
|
|
},
|
||
|
|
|
||
|
|
detached() {
|
||
|
|
console.log('🎨 媒体预览组件已卸载');
|
||
|
|
this.cleanup();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
methods: {
|
||
|
|
// 🎨 ===== 基础操作 =====
|
||
|
|
|
||
|
|
// 阻止事件冒泡
|
||
|
|
stopPropagation() {
|
||
|
|
// 阻止点击事件冒泡到遮罩层
|
||
|
|
},
|
||
|
|
|
||
|
|
// 遮罩点击
|
||
|
|
onMaskTap() {
|
||
|
|
this.closePreview();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 关闭预览
|
||
|
|
closePreview() {
|
||
|
|
this.setData({
|
||
|
|
visible: false
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('close');
|
||
|
|
this.cleanup();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 清理资源
|
||
|
|
cleanup() {
|
||
|
|
// 停止音频播放
|
||
|
|
if (this.data.audioPlaying) {
|
||
|
|
this.stopAudio();
|
||
|
|
}
|
||
|
|
|
||
|
|
// 清理定时器
|
||
|
|
if (this.data.gestureTimer) {
|
||
|
|
clearTimeout(this.data.gestureTimer);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 图片操作 =====
|
||
|
|
|
||
|
|
// 轮播图切换
|
||
|
|
onSwiperChange(e) {
|
||
|
|
const currentIndex = e.detail.current;
|
||
|
|
this.setData({
|
||
|
|
currentIndex: currentIndex
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('indexchange', {
|
||
|
|
currentIndex: currentIndex
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 图片加载完成
|
||
|
|
onImageLoad(e) {
|
||
|
|
console.log('🖼️ 图片加载完成');
|
||
|
|
|
||
|
|
const index = e.currentTarget.dataset.index;
|
||
|
|
const mediaList = this.data.mediaList;
|
||
|
|
|
||
|
|
if (mediaList[index]) {
|
||
|
|
mediaList[index].loading = false;
|
||
|
|
mediaList[index].error = false;
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
mediaList: mediaList
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 图片加载失败
|
||
|
|
onImageError(e) {
|
||
|
|
console.error('❌ 图片加载失败');
|
||
|
|
|
||
|
|
const index = e.currentTarget.dataset.index;
|
||
|
|
const mediaList = this.data.mediaList;
|
||
|
|
|
||
|
|
if (mediaList[index]) {
|
||
|
|
mediaList[index].loading = false;
|
||
|
|
mediaList[index].error = true;
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
mediaList: mediaList
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 图片点击
|
||
|
|
onImageTap(e) {
|
||
|
|
// 可以实现双击放大等功能
|
||
|
|
console.log('🖼️ 图片点击');
|
||
|
|
},
|
||
|
|
|
||
|
|
// 重试加载
|
||
|
|
retryLoad(e) {
|
||
|
|
const index = e.currentTarget.dataset.index;
|
||
|
|
const mediaList = this.data.mediaList;
|
||
|
|
|
||
|
|
if (mediaList[index]) {
|
||
|
|
mediaList[index].loading = true;
|
||
|
|
mediaList[index].error = false;
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
mediaList: mediaList
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 视频操作 =====
|
||
|
|
|
||
|
|
// 视频播放
|
||
|
|
onVideoPlay() {
|
||
|
|
console.log('🎬 视频开始播放');
|
||
|
|
this.triggerEvent('videoplay');
|
||
|
|
},
|
||
|
|
|
||
|
|
// 视频暂停
|
||
|
|
onVideoPause() {
|
||
|
|
console.log('🎬 视频暂停');
|
||
|
|
this.triggerEvent('videopause');
|
||
|
|
},
|
||
|
|
|
||
|
|
// 视频结束
|
||
|
|
onVideoEnded() {
|
||
|
|
console.log('🎬 视频播放结束');
|
||
|
|
this.triggerEvent('videoended');
|
||
|
|
},
|
||
|
|
|
||
|
|
// 视频错误
|
||
|
|
onVideoError(e) {
|
||
|
|
console.error('❌ 视频播放错误:', e.detail);
|
||
|
|
wx.showToast({
|
||
|
|
title: '视频播放失败',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 视频时间更新
|
||
|
|
onVideoTimeUpdate(e) {
|
||
|
|
// 可以用于显示播放进度
|
||
|
|
console.log('🎬 视频时间更新:', e.detail);
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 音频操作 =====
|
||
|
|
|
||
|
|
// 切换音频播放
|
||
|
|
toggleAudioPlay() {
|
||
|
|
if (this.data.audioPlaying) {
|
||
|
|
this.pauseAudio();
|
||
|
|
} else {
|
||
|
|
this.playAudio();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 播放音频
|
||
|
|
playAudio() {
|
||
|
|
// 这里需要实现音频播放逻辑
|
||
|
|
console.log('🎵 播放音频');
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
audioPlaying: true
|
||
|
|
});
|
||
|
|
|
||
|
|
// 模拟播放进度
|
||
|
|
this.startAudioProgress();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 暂停音频
|
||
|
|
pauseAudio() {
|
||
|
|
console.log('🎵 暂停音频');
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
audioPlaying: false
|
||
|
|
});
|
||
|
|
|
||
|
|
this.stopAudioProgress();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 停止音频
|
||
|
|
stopAudio() {
|
||
|
|
console.log('🎵 停止音频');
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
audioPlaying: false,
|
||
|
|
audioProgress: 0,
|
||
|
|
audioCurrentTime: 0
|
||
|
|
});
|
||
|
|
|
||
|
|
this.stopAudioProgress();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 开始音频进度更新
|
||
|
|
startAudioProgress() {
|
||
|
|
this.audioProgressTimer = setInterval(() => {
|
||
|
|
const currentTime = this.data.audioCurrentTime + 1;
|
||
|
|
const duration = this.data.currentMedia.duration || 100;
|
||
|
|
const progress = (currentTime / duration) * 100;
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
audioCurrentTime: currentTime,
|
||
|
|
audioProgress: Math.min(progress, 100)
|
||
|
|
});
|
||
|
|
|
||
|
|
if (progress >= 100) {
|
||
|
|
this.stopAudio();
|
||
|
|
}
|
||
|
|
}, 1000);
|
||
|
|
},
|
||
|
|
|
||
|
|
// 停止音频进度更新
|
||
|
|
stopAudioProgress() {
|
||
|
|
if (this.audioProgressTimer) {
|
||
|
|
clearInterval(this.audioProgressTimer);
|
||
|
|
this.audioProgressTimer = null;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 文件操作 =====
|
||
|
|
|
||
|
|
// 打开文件
|
||
|
|
openFile() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
wx.openDocument({
|
||
|
|
filePath: currentMedia.tempFilePath || currentMedia.url,
|
||
|
|
fileType: currentMedia.extension,
|
||
|
|
success: () => {
|
||
|
|
console.log('📄 文件打开成功');
|
||
|
|
},
|
||
|
|
fail: (error) => {
|
||
|
|
console.error('❌ 文件打开失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: '无法打开此文件',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 保存文件
|
||
|
|
async saveFile() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
try {
|
||
|
|
wx.showLoading({
|
||
|
|
title: '保存中...'
|
||
|
|
});
|
||
|
|
|
||
|
|
// 如果是网络文件,先下载
|
||
|
|
let filePath = currentMedia.tempFilePath;
|
||
|
|
if (!filePath && currentMedia.url) {
|
||
|
|
const downloadResult = await mediaManager.downloadFile(currentMedia.url);
|
||
|
|
if (downloadResult.success) {
|
||
|
|
filePath = downloadResult.tempFilePath;
|
||
|
|
} else {
|
||
|
|
throw new Error('下载失败');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 保存到本地
|
||
|
|
const result = await new Promise((resolve, reject) => {
|
||
|
|
wx.saveFile({
|
||
|
|
tempFilePath: filePath,
|
||
|
|
success: resolve,
|
||
|
|
fail: reject
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
wx.hideLoading();
|
||
|
|
wx.showToast({
|
||
|
|
title: '保存成功',
|
||
|
|
icon: 'success'
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('📄 文件保存成功:', result.savedFilePath);
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
wx.hideLoading();
|
||
|
|
console.error('❌ 文件保存失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: '保存失败',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 操作按钮 =====
|
||
|
|
|
||
|
|
// 下载媒体
|
||
|
|
async downloadMedia() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
if (!currentMedia.url) {
|
||
|
|
wx.showToast({
|
||
|
|
title: '无法下载',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
wx.showLoading({
|
||
|
|
title: '下载中...'
|
||
|
|
});
|
||
|
|
|
||
|
|
const result = await mediaManager.downloadFile(currentMedia.url, {
|
||
|
|
fileName: currentMedia.name
|
||
|
|
});
|
||
|
|
|
||
|
|
wx.hideLoading();
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
wx.showToast({
|
||
|
|
title: '下载完成',
|
||
|
|
icon: 'success'
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('download', {
|
||
|
|
media: currentMedia,
|
||
|
|
filePath: result.tempFilePath
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
throw new Error(result.error);
|
||
|
|
}
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
wx.hideLoading();
|
||
|
|
console.error('❌ 下载失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: '下载失败',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 分享媒体
|
||
|
|
shareMedia() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
this.triggerEvent('share', {
|
||
|
|
media: currentMedia
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 编辑媒体
|
||
|
|
editMedia() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
this.triggerEvent('edit', {
|
||
|
|
media: currentMedia,
|
||
|
|
index: this.data.currentIndex
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 删除媒体
|
||
|
|
deleteMedia() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
|
||
|
|
wx.showModal({
|
||
|
|
title: '删除确认',
|
||
|
|
content: '确定要删除这个文件吗?',
|
||
|
|
success: (res) => {
|
||
|
|
if (res.confirm) {
|
||
|
|
this.triggerEvent('delete', {
|
||
|
|
media: currentMedia,
|
||
|
|
index: this.data.currentIndex
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 收藏媒体
|
||
|
|
favoriteMedia() {
|
||
|
|
const currentMedia = this.data.currentMedia;
|
||
|
|
const favorited = !currentMedia.favorited;
|
||
|
|
|
||
|
|
// 更新收藏状态
|
||
|
|
currentMedia.favorited = favorited;
|
||
|
|
this.setData({
|
||
|
|
currentMedia: currentMedia
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('favorite', {
|
||
|
|
media: currentMedia,
|
||
|
|
favorited: favorited
|
||
|
|
});
|
||
|
|
|
||
|
|
wx.showToast({
|
||
|
|
title: favorited ? '已收藏' : '已取消收藏',
|
||
|
|
icon: 'success'
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 显示更多操作
|
||
|
|
showMoreActions() {
|
||
|
|
const actions = ['转发', '设为壁纸', '添加到相册', '举报'];
|
||
|
|
|
||
|
|
wx.showActionSheet({
|
||
|
|
itemList: actions,
|
||
|
|
success: (res) => {
|
||
|
|
this.triggerEvent('moreaction', {
|
||
|
|
action: actions[res.tapIndex],
|
||
|
|
media: this.data.currentMedia
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 工具方法 =====
|
||
|
|
|
||
|
|
// 格式化文件大小
|
||
|
|
formatFileSize(size) {
|
||
|
|
if (!size) return '未知大小';
|
||
|
|
|
||
|
|
const units = ['B', 'KB', 'MB', 'GB'];
|
||
|
|
let unitIndex = 0;
|
||
|
|
let fileSize = size;
|
||
|
|
|
||
|
|
while (fileSize >= 1024 && unitIndex < units.length - 1) {
|
||
|
|
fileSize /= 1024;
|
||
|
|
unitIndex++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return `${fileSize.toFixed(1)} ${units[unitIndex]}`;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 格式化时长
|
||
|
|
formatDuration(duration) {
|
||
|
|
if (!duration) return '00:00';
|
||
|
|
|
||
|
|
const minutes = Math.floor(duration / 60);
|
||
|
|
const seconds = Math.floor(duration % 60);
|
||
|
|
|
||
|
|
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 格式化时间
|
||
|
|
formatTime(time) {
|
||
|
|
if (!time) return '00:00';
|
||
|
|
|
||
|
|
const minutes = Math.floor(time / 60);
|
||
|
|
const seconds = Math.floor(time % 60);
|
||
|
|
|
||
|
|
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 获取文件图标
|
||
|
|
getFileIcon(extension) {
|
||
|
|
const iconMap = {
|
||
|
|
'pdf': '📄',
|
||
|
|
'doc': '📝',
|
||
|
|
'docx': '📝',
|
||
|
|
'xls': '📊',
|
||
|
|
'xlsx': '📊',
|
||
|
|
'ppt': '📽️',
|
||
|
|
'pptx': '📽️',
|
||
|
|
'txt': '📃',
|
||
|
|
'zip': '🗜️',
|
||
|
|
'rar': '🗜️',
|
||
|
|
'mp3': '🎵',
|
||
|
|
'wav': '🎵',
|
||
|
|
'mp4': '🎬',
|
||
|
|
'avi': '🎬'
|
||
|
|
};
|
||
|
|
|
||
|
|
return iconMap[extension] || '📄';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|