miniprogramme/components/message-action-menu/message-action-menu.js

547 lines
13 KiB
JavaScript
Raw Normal View History

2025-09-12 16:08:17 +08:00
// ✨ 消息操作菜单组件逻辑
const messageInteractionManager = require('../../utils/message-interaction-manager.js');
Component({
properties: {
// 是否显示菜单
visible: {
type: Boolean,
value: false
},
// 消息对象
message: {
type: Object,
value: {}
},
// 是否是自己的消息
isOwnMessage: {
type: Boolean,
value: false
},
// 可用的操作
actions: {
type: Object,
value: {
quote: true, // 引用回复
forward: true, // 转发
favorite: true, // 收藏
multiSelect: true, // 多选
copy: true, // 复制
recall: true, // 撤回
delete: true, // 删除
report: true // 举报
}
},
// 是否显示表情回应
showReactions: {
type: Boolean,
value: true
},
// 是否显示消息信息
showMessageInfo: {
type: Boolean,
value: false
}
},
data: {
// 常用表情
commonEmojis: ['👍', '❤️', '😂', '😮', '😢', '😡'],
// 是否可以撤回
canRecall: false,
// 表情选择器
showEmojiPicker: false,
currentEmojiCategory: 'recent',
currentEmojiList: [],
// 表情分类
emojiCategories: {
recent: ['👍', '❤️', '😂', '😮', '😢', '😡', '🎉', '🔥'],
smileys: ['😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚', '😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🤩', '🥳'],
gestures: ['👍', '👎', '👌', '✌️', '🤞', '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👋', '🤚', '🖐️', '✋', '🖖', '👏', '🙌', '🤲', '🤝', '🙏'],
hearts: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝', '💟']
}
},
observers: {
'message, isOwnMessage': function(message, isOwnMessage) {
if (message && message.messageId) {
this.checkRecallPermission();
}
}
},
lifetimes: {
attached() {
console.log('✨ 消息操作菜单组件已加载');
this.initEmojiList();
}
},
methods: {
// ✨ ===== 基础操作 =====
// 阻止事件冒泡
stopPropagation() {
// 阻止点击事件冒泡到遮罩层
},
// 遮罩点击
onMaskTap() {
this.closeMenu();
},
// 关闭菜单
closeMenu() {
this.setData({
visible: false,
showEmojiPicker: false
});
this.triggerEvent('close');
},
// 👍 ===== 表情回应操作 =====
// 表情点击
async onReactionTap(e) {
const emoji = e.currentTarget.dataset.emoji;
console.log('👍 表情点击:', emoji);
try {
const userId = wx.getStorageSync('userId');
if (!userId) {
wx.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
// 添加表情回应
const result = await messageInteractionManager.addReaction(
this.data.message.messageId,
emoji,
userId
);
if (result.success) {
// 触发表情回应事件
this.triggerEvent('reaction', {
messageId: this.data.message.messageId,
emoji: emoji,
action: 'add'
});
// 关闭菜单
this.closeMenu();
wx.showToast({
title: '表情回应已添加',
icon: 'success'
});
} else {
wx.showToast({
title: result.error || '添加失败',
icon: 'none'
});
}
} catch (error) {
console.error('❌ 添加表情回应失败:', error);
wx.showToast({
title: '操作失败',
icon: 'none'
});
}
},
// 显示更多表情
showMoreEmojis() {
this.setData({
showEmojiPicker: true,
currentEmojiCategory: 'recent'
});
this.updateEmojiList();
},
// 关闭表情选择器
closeEmojiPicker() {
this.setData({
showEmojiPicker: false
});
},
// 切换表情分类
switchEmojiCategory(e) {
const category = e.currentTarget.dataset.category;
this.setData({
currentEmojiCategory: category
});
this.updateEmojiList();
},
// 表情选择
async onEmojiSelect(e) {
const emoji = e.currentTarget.dataset.emoji;
// 添加到最近使用
this.addToRecentEmojis(emoji);
// 执行表情回应
await this.onReactionTap({ currentTarget: { dataset: { emoji } } });
},
// 初始化表情列表
initEmojiList() {
this.setData({
currentEmojiList: this.data.emojiCategories.recent
});
},
// 更新表情列表
updateEmojiList() {
const category = this.data.currentEmojiCategory;
const emojiList = this.data.emojiCategories[category] || [];
this.setData({
currentEmojiList: emojiList
});
},
// 添加到最近使用表情
addToRecentEmojis(emoji) {
let recentEmojis = [...this.data.emojiCategories.recent];
// 移除已存在的
recentEmojis = recentEmojis.filter(e => e !== emoji);
// 添加到开头
recentEmojis.unshift(emoji);
// 限制数量
if (recentEmojis.length > 20) {
recentEmojis = recentEmojis.slice(0, 20);
}
// 更新数据
this.setData({
[`emojiCategories.recent`]: recentEmojis
});
// 如果当前显示的是最近分类,更新列表
if (this.data.currentEmojiCategory === 'recent') {
this.setData({
currentEmojiList: recentEmojis
});
}
},
// 🎯 ===== 操作按钮处理 =====
// 操作点击
async onActionTap(e) {
const action = e.currentTarget.dataset.action;
console.log('🎯 操作点击:', action);
switch (action) {
case 'quote':
this.handleQuote();
break;
case 'forward':
this.handleForward();
break;
case 'favorite':
this.handleFavorite();
break;
case 'multiSelect':
this.handleMultiSelect();
break;
case 'copy':
this.handleCopy();
break;
case 'recall':
this.handleRecall();
break;
case 'delete':
this.handleDelete();
break;
case 'report':
this.handleReport();
break;
default:
console.warn('⚠️ 未知操作:', action);
}
},
// 处理引用回复
handleQuote() {
console.log('💬 处理引用回复');
this.triggerEvent('action', {
action: 'quote',
message: this.data.message
});
this.closeMenu();
},
// 处理转发
handleForward() {
console.log('📤 处理转发');
this.triggerEvent('action', {
action: 'forward',
message: this.data.message
});
this.closeMenu();
},
// 处理收藏
async handleFavorite() {
console.log('⭐ 处理收藏');
try {
const userId = wx.getStorageSync('userId');
const messageId = this.data.message.messageId;
const isFavorited = this.data.message.favorited;
let result;
if (isFavorited) {
result = await messageInteractionManager.unfavoriteMessage(messageId, userId);
} else {
result = await messageInteractionManager.favoriteMessage(messageId, userId);
}
if (result.success) {
this.triggerEvent('action', {
action: 'favorite',
message: this.data.message,
favorited: !isFavorited
});
wx.showToast({
title: isFavorited ? '已取消收藏' : '已收藏',
icon: 'success'
});
} else {
wx.showToast({
title: result.error || '操作失败',
icon: 'none'
});
}
} catch (error) {
console.error('❌ 收藏操作失败:', error);
wx.showToast({
title: '操作失败',
icon: 'none'
});
}
this.closeMenu();
},
// 处理多选
handleMultiSelect() {
console.log('📋 处理多选');
this.triggerEvent('action', {
action: 'multiSelect',
message: this.data.message
});
this.closeMenu();
},
// 处理复制
handleCopy() {
console.log('📄 处理复制');
if (this.data.message.msgType === 'text') {
wx.setClipboardData({
data: this.data.message.content,
success: () => {
wx.showToast({
title: '已复制到剪贴板',
icon: 'success'
});
}
});
}
this.closeMenu();
},
// 处理撤回
async handleRecall() {
console.log('🔄 处理撤回');
try {
const userId = wx.getStorageSync('userId');
const messageId = this.data.message.messageId;
const result = await messageInteractionManager.recallMessage(messageId, userId);
if (result.success) {
this.triggerEvent('action', {
action: 'recall',
message: this.data.message
});
wx.showToast({
title: '消息已撤回',
icon: 'success'
});
} else {
wx.showToast({
title: result.error || '撤回失败',
icon: 'none'
});
}
} catch (error) {
console.error('❌ 撤回消息失败:', error);
wx.showToast({
title: '撤回失败',
icon: 'none'
});
}
this.closeMenu();
},
// 处理删除
handleDelete() {
console.log('🗑️ 处理删除');
wx.showModal({
title: '删除消息',
content: '确定要删除这条消息吗?',
success: (res) => {
if (res.confirm) {
this.triggerEvent('action', {
action: 'delete',
message: this.data.message
});
}
}
});
this.closeMenu();
},
// 处理举报
handleReport() {
console.log('⚠️ 处理举报');
wx.showActionSheet({
itemList: ['垃圾信息', '违法违规', '色情内容', '暴力内容', '其他'],
success: (res) => {
const reasons = ['spam', 'illegal', 'sexual', 'violence', 'other'];
const reason = reasons[res.tapIndex];
this.triggerEvent('action', {
action: 'report',
message: this.data.message,
reason: reason
});
}
});
this.closeMenu();
},
// 🔧 ===== 工具方法 =====
// 检查撤回权限
async checkRecallPermission() {
try {
const userId = wx.getStorageSync('userId');
const messageId = this.data.message.messageId;
if (!userId || !messageId) {
this.setData({ canRecall: false });
return;
}
const result = await messageInteractionManager.checkRecallPermission(messageId, userId);
this.setData({ canRecall: result.allowed });
} catch (error) {
console.error('❌ 检查撤回权限失败:', error);
this.setData({ canRecall: false });
}
},
// 格式化时间
formatTime(timestamp) {
if (!timestamp) return '';
const date = new Date(timestamp);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
// 今天
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
} else if (diffDays === 1) {
// 昨天
return '昨天 ' + date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
} else {
// 更早
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
}
},
// 获取消息类型文本
getMessageTypeText(msgType) {
const typeMap = {
'text': '文本',
'image': '图片',
'video': '视频',
'voice': '语音',
'file': '文件',
'location': '位置',
'card': '名片'
};
return typeMap[msgType] || '未知';
},
// 格式化文件大小
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]}`;
}
}
});