373 lines
11 KiB
JavaScript
373 lines
11 KiB
JavaScript
const { resolveAvatarUrl, getDefaultAvatarUrl } = require('../../utils/avatar.js');
|
|
|
|
Component({
|
|
properties: {
|
|
position: {
|
|
type: String,
|
|
value: 'top'
|
|
},
|
|
duration: {
|
|
type: Number,
|
|
value: 3000
|
|
},
|
|
showProgress: {
|
|
type: Boolean,
|
|
value: true
|
|
},
|
|
showMuteButton: {
|
|
type: Boolean,
|
|
value: true
|
|
},
|
|
showTime: {
|
|
type: Boolean,
|
|
value: true
|
|
},
|
|
swipeToClose: {
|
|
type: Boolean,
|
|
value: true
|
|
},
|
|
swipeThreshold: {
|
|
type: Number,
|
|
value: 50
|
|
}
|
|
},
|
|
|
|
data: {
|
|
show: false,
|
|
title: '',
|
|
message: '',
|
|
avatar: '',
|
|
notificationData: null,
|
|
progress: 100,
|
|
progressInterval: null,
|
|
positionClass: 'top',
|
|
|
|
touchStartY: 0,
|
|
touchStartX: 0,
|
|
currentY: 0,
|
|
currentX: 0,
|
|
isSwiping: false,
|
|
swipingClass: '',
|
|
swipeStyle: '',
|
|
swipeDistance: 0,
|
|
|
|
// 静音状态
|
|
isMuted: false,
|
|
formattedTime: '',
|
|
|
|
// 自动隐藏计时器
|
|
autoHideTimeout: null
|
|
},
|
|
|
|
lifetimes: {
|
|
attached() {
|
|
this.loadMuteSetting();
|
|
this.setData({
|
|
positionClass: this.properties.position
|
|
});
|
|
this.setupDirectEventListeners();
|
|
},
|
|
|
|
detached() {
|
|
this.clearTimers();
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
setupDirectEventListeners() {
|
|
const app = getApp();
|
|
if (app) {
|
|
const self = this;
|
|
const globalNotification = {
|
|
show: (data) => {
|
|
if (self && self.showNotification) {
|
|
self.showNotification(data);
|
|
}
|
|
},
|
|
hide: () => {
|
|
if (self && self.hideNotification) {
|
|
self.hideNotification();
|
|
}
|
|
},
|
|
isMuted: () => {
|
|
return self.data.isMuted;
|
|
}
|
|
};
|
|
app.globalData.globalNotification = globalNotification;
|
|
}
|
|
},
|
|
|
|
loadMuteSetting() {
|
|
try {
|
|
const isMuted = wx.getStorageSync('notificationMuted') || false;
|
|
this.setData({ isMuted });
|
|
} catch (error) {
|
|
console.warn('加载静音设置失败:', error);
|
|
}
|
|
},
|
|
|
|
saveMuteSetting(isMuted) {
|
|
try {
|
|
wx.setStorageSync('notificationMuted', isMuted);
|
|
} catch (error) {
|
|
console.warn('保存静音设置失败:', error);
|
|
}
|
|
},
|
|
|
|
showNotification(data) {
|
|
if (!data) return;
|
|
if (this.data.isMuted) return;
|
|
|
|
this.clearTimers();
|
|
|
|
const app = getApp();
|
|
let avatar = resolveAvatarUrl(
|
|
data.avatar || data.message?.senderData?.avatar,
|
|
app
|
|
) || getDefaultAvatarUrl(app);
|
|
|
|
this.setData({
|
|
show: true,
|
|
title: data.senderName || '新消息',
|
|
message: data.content || '[消息内容]',
|
|
avatar: avatar,
|
|
notificationData: data,
|
|
formattedTime: this.formatTime(data.timestamp),
|
|
progress: 100,
|
|
swipingClass: '',
|
|
swipeStyle: ''
|
|
});
|
|
|
|
if (this.properties.showProgress) {
|
|
this.startProgressBar(this.properties.duration);
|
|
}
|
|
|
|
this.data.autoHideTimeout = setTimeout(() => {
|
|
this.hideNotification();
|
|
}, this.properties.duration);
|
|
},
|
|
|
|
hideNotification() {
|
|
this.clearTimers();
|
|
this.setData({
|
|
show: false,
|
|
formattedTime: '',
|
|
progress: 100,
|
|
swipingClass: '',
|
|
swipeStyle: ''
|
|
});
|
|
this.triggerEvent('hide');
|
|
},
|
|
|
|
startProgressBar(duration) {
|
|
const interval = 100;
|
|
const steps = duration / interval;
|
|
const stepValue = 100 / steps;
|
|
|
|
let currentProgress = 100;
|
|
this.data.progressInterval = setInterval(() => {
|
|
currentProgress -= stepValue;
|
|
if (currentProgress <= 0) {
|
|
currentProgress = 0;
|
|
this.setData({ progress: currentProgress });
|
|
clearInterval(this.data.progressInterval);
|
|
} else {
|
|
this.setData({ progress: currentProgress });
|
|
}
|
|
}, interval);
|
|
},
|
|
|
|
clearTimers() {
|
|
if (this.data.autoHideTimeout) {
|
|
clearTimeout(this.data.autoHideTimeout);
|
|
this.data.autoHideTimeout = null;
|
|
}
|
|
if (this.data.progressInterval) {
|
|
clearInterval(this.data.progressInterval);
|
|
this.data.progressInterval = null;
|
|
}
|
|
},
|
|
|
|
onTouchStart(e) {
|
|
if (!this.properties.swipeToClose) return;
|
|
const touch = e.touches[0];
|
|
this.setData({
|
|
touchStartY: touch.clientY,
|
|
touchStartX: touch.clientX,
|
|
currentY: touch.clientY,
|
|
currentX: touch.clientX,
|
|
isSwiping: false
|
|
});
|
|
this.clearTimers();
|
|
},
|
|
|
|
onTouchMove(e) {
|
|
if (!this.properties.swipeToClose) return;
|
|
const touch = e.touches[0];
|
|
const deltaY = touch.clientY - this.data.touchStartY;
|
|
const deltaX = touch.clientX - this.data.touchStartX;
|
|
const absDeltaY = Math.abs(deltaY);
|
|
const absDeltaX = Math.abs(deltaX);
|
|
|
|
if (absDeltaY > 10 && absDeltaY > absDeltaX) {
|
|
this.setData({
|
|
isSwiping: true,
|
|
currentY: touch.clientY,
|
|
swipingClass: deltaY < 0 ? 'swiping-up' : 'swiping',
|
|
swipeDistance: Math.abs(deltaY),
|
|
swipeStyle: `--swipe-distance: ${Math.abs(deltaY)}rpx;`
|
|
});
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
|
|
onTouchEnd(e) {
|
|
if (!this.properties.swipeToClose || !this.data.isSwiping) {
|
|
this.restartAutoHide();
|
|
return;
|
|
}
|
|
const deltaY = this.data.currentY - this.data.touchStartY;
|
|
const absDeltaY = Math.abs(deltaY);
|
|
if (absDeltaY > this.properties.swipeThreshold) {
|
|
this.hideNotification();
|
|
} else {
|
|
this.setData({
|
|
swipingClass: '',
|
|
swipeStyle: 'transition: transform 0.3s ease; transform: translateY(0);'
|
|
});
|
|
setTimeout(() => {
|
|
this.setData({
|
|
swipingClass: '',
|
|
swipeStyle: ''
|
|
});
|
|
}, 300);
|
|
this.restartAutoHide();
|
|
}
|
|
this.setData({ isSwiping: false });
|
|
},
|
|
|
|
restartAutoHide() {
|
|
const remainingTime = (this.data.progress / 100) * this.properties.duration;
|
|
if (remainingTime > 0) {
|
|
this.data.autoHideTimeout = setTimeout(() => {
|
|
this.hideNotification();
|
|
}, remainingTime);
|
|
}
|
|
},
|
|
|
|
onTap() {
|
|
const { notificationData } = this.data;
|
|
if (!notificationData) return;
|
|
this.triggerEvent('tap', notificationData);
|
|
this.hideNotification();
|
|
this.navigateToChatPage(notificationData);
|
|
},
|
|
|
|
onClose(e) {
|
|
e.stopPropagation();
|
|
this.triggerEvent('close');
|
|
this.hideNotification();
|
|
},
|
|
|
|
onAvatarError() {
|
|
const def = getDefaultAvatarUrl(getApp());
|
|
if (def && this.data.avatar !== def) {
|
|
this.setData({ avatar: def });
|
|
}
|
|
},
|
|
|
|
onMute(e) {
|
|
e.stopPropagation();
|
|
const newMutedState = !this.data.isMuted;
|
|
this.setData({ isMuted: newMutedState });
|
|
this.saveMuteSetting(newMutedState);
|
|
|
|
const app = getApp();
|
|
if (app && app.toggleDoNotDisturb) {
|
|
app.toggleDoNotDisturb(newMutedState);
|
|
}
|
|
this.triggerEvent('mute', { muted: newMutedState });
|
|
wx.showToast({
|
|
title: newMutedState ? '已开启免打扰' : '已关闭免打扰',
|
|
icon: 'success',
|
|
duration: 1500
|
|
});
|
|
|
|
if (newMutedState) {
|
|
this.hideNotification();
|
|
}
|
|
},
|
|
|
|
formatTime(timestamp) {
|
|
if (!timestamp) return '';
|
|
const date = new Date(timestamp);
|
|
const now = new Date();
|
|
const diff = now - date;
|
|
if (diff < 60 * 1000) return '刚刚';
|
|
if (diff < 60 * 60 * 1000) return Math.floor(diff / (60 * 1000)) + '分钟前';
|
|
if (date.toDateString() === now.toDateString()) {
|
|
return date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
|
|
}
|
|
const yesterday = new Date(now);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
if (date.toDateString() === yesterday.toDateString()) {
|
|
return '昨天 ' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
|
|
}
|
|
return (date.getMonth() + 1) + '月' + date.getDate() + '日';
|
|
},
|
|
|
|
navigateToChatPage(data) {
|
|
if (!data || !data.message) {
|
|
console.warn('通知数据无效,无法跳转');
|
|
return;
|
|
}
|
|
|
|
const msg = data.message;
|
|
let path = '';
|
|
let queryParam = null;
|
|
|
|
if (data.notificationType === 'cs' || msg.teamId) {
|
|
const teamId = data.teamId || msg.teamId;
|
|
path = '/pages/cs-chat/cs-chat';
|
|
queryParam = { teamId: teamId };
|
|
} else if (data.notificationType === 'group' || msg.groupId) {
|
|
const groupId = data.groupId || msg.groupId;
|
|
const orderId = data.orderId || msg.orderId || '';
|
|
const groupName = data.groupName || '订单群聊';
|
|
const groupAvatar = data.groupAvatar || '';
|
|
const isCross = data.isCross || msg.isCross || 0;
|
|
path = '/pages/group-chat/group-chat';
|
|
queryParam = {
|
|
groupId,
|
|
orderId,
|
|
groupName,
|
|
groupAvatar,
|
|
isCross
|
|
};
|
|
} else {
|
|
const toUserId = data.senderId || msg.senderId;
|
|
const toName = data.senderName || '用户';
|
|
const toAvatar = data.avatar || '';
|
|
path = '/pages/chat/chat';
|
|
queryParam = {
|
|
toUserId,
|
|
toName,
|
|
toAvatar
|
|
};
|
|
}
|
|
|
|
if (path && queryParam) {
|
|
const encoded = encodeURIComponent(JSON.stringify(queryParam));
|
|
wx.navigateTo({
|
|
url: `${path}?data=${encoded}`,
|
|
fail: (err) => {
|
|
console.error('通知跳转失败:', err);
|
|
wx.switchTab({ url: '/pages/messages/messages' });
|
|
}
|
|
});
|
|
} else {
|
|
wx.switchTab({ url: '/pages/messages/messages' });
|
|
}
|
|
}
|
|
}
|
|
}); |