Files
a_long/miniprogram/components/global-notification/global-notification.js
2026-06-27 01:26:11 +08:00

366 lines
10 KiB
JavaScript

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,
// 自动隐藏计时器
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 = data.avatar;
if (!avatar && app && app.globalData) {
avatar = app.globalData.ossImageUrl + app.globalData.morentouxiang;
}
this.setData({
show: true,
title: data.senderName || '新消息',
message: data.content || '[消息内容]',
avatar: avatar,
notificationData: data,
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,
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();
},
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;
// 根据 notificationType 或消息字段判断
if (data.notificationType === 'cs' || msg.teamId) {
// 客服消息
const teamId = data.teamId || msg.teamId;
path = '/pages/kefuliaotian/kefuliaotian';
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/qunliaotian/qunliaotian';
queryParam = {
groupId,
orderId,
groupName,
groupAvatar,
isCross
};
} else {
// 默认私聊
const toUserId = data.senderId || msg.senderId;
const toName = data.senderName || '用户';
const toAvatar = data.avatar || '';
path = '/pages/liaotian/liaotian';
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/xiaoxi/xiaoxi' });
}
});
} else {
wx.switchTab({ url: '/pages/xiaoxi/xiaoxi' });
}
}
}
});