2026-01-07 14:21:29 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view v-if="visible" class="notification-wrapper" :class="{ 'slide-in': visible }" :style="{ paddingTop: notificationTop + 'px' }">
|
|
|
|
|
|
<view class="notification" @click="handleClick">
|
|
|
|
|
|
<!-- 头像 -->
|
2026-05-15 16:42:32 +08:00
|
|
|
|
<image class="avatar" :src="avatar || 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/xx/ysxx.png'" mode="aspectFill" />
|
2026-01-07 14:21:29 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 内容区域 -->
|
|
|
|
|
|
<view class="content">
|
|
|
|
|
|
<text class="title">{{ title || '新消息' }}</text>
|
|
|
|
|
|
<text class="message">{{ displayMessage }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 关闭按钮 -->
|
|
|
|
|
|
<view class="close" @click.stop="handleClose">
|
|
|
|
|
|
<text class="close-icon">×</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'MessageNotification',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
avatar: '',
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
message: '',
|
|
|
|
|
|
roomId: '',
|
|
|
|
|
|
roomData: null,
|
|
|
|
|
|
autoHideTimer: null,
|
|
|
|
|
|
notificationTop: 50 // 默认值
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
// 获取胶囊按钮位置,计算通知的安全位置
|
|
|
|
|
|
this.calculateSafeTop();
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
displayMessage() {
|
|
|
|
|
|
// 限制消息长度,超过30个字符显示省略号
|
|
|
|
|
|
if (this.message && this.message.length > 30) {
|
|
|
|
|
|
return this.message.substring(0, 30) + '...';
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.message || '您有一条新消息';
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
// 监听显示通知事件
|
|
|
|
|
|
uni.$on('show-message-notification', this.showNotification);
|
|
|
|
|
|
// 监听隐藏通知事件
|
|
|
|
|
|
uni.$on('hide-message-notification', this.hideNotification);
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
// 移除事件监听
|
|
|
|
|
|
uni.$off('show-message-notification', this.showNotification);
|
|
|
|
|
|
uni.$off('hide-message-notification', this.hideNotification);
|
|
|
|
|
|
// 清除定时器
|
|
|
|
|
|
if (this.autoHideTimer) {
|
|
|
|
|
|
clearTimeout(this.autoHideTimer);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算通知的安全顶部位置(避开胶囊按钮)
|
|
|
|
|
|
*/
|
|
|
|
|
|
calculateSafeTop() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// #ifdef MP-WEIXIN
|
|
|
|
|
|
// 获取胶囊按钮的位置信息
|
|
|
|
|
|
const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
|
|
|
|
|
|
// 通知顶部 = 胶囊底部 + 10px 安全边距
|
|
|
|
|
|
this.notificationTop = menuButtonInfo.bottom + 10;
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// #ifndef MP-WEIXIN
|
|
|
|
|
|
// 非微信小程序环境,使用状态栏高度 + 安全边距
|
|
|
|
|
|
const systemInfo = uni.getSystemInfoSync();
|
|
|
|
|
|
this.notificationTop = (systemInfo.statusBarHeight || 44) + 10;
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// 获取失败时使用默认值
|
|
|
|
|
|
this.notificationTop = 50;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示通知
|
|
|
|
|
|
* @param {Object} data 通知数据
|
|
|
|
|
|
* @param {string} data.avatar 头像
|
|
|
|
|
|
* @param {string} data.title 标题(发送者昵称)
|
|
|
|
|
|
* @param {string} data.message 消息内容
|
|
|
|
|
|
* @param {string} data.roomId 房间ID
|
|
|
|
|
|
* @param {Object} data.roomData 房间数据(用于跳转)
|
|
|
|
|
|
*/
|
|
|
|
|
|
showNotification(data) {
|
|
|
|
|
|
// 清除之前的定时器
|
|
|
|
|
|
if (this.autoHideTimer) {
|
|
|
|
|
|
clearTimeout(this.autoHideTimer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置通知数据
|
|
|
|
|
|
this.avatar = data.avatar || '';
|
|
|
|
|
|
this.title = data.title || '新消息';
|
|
|
|
|
|
this.message = data.message || '您有一条新消息';
|
|
|
|
|
|
this.roomId = data.roomId || '';
|
|
|
|
|
|
this.roomData = data.roomData || null;
|
|
|
|
|
|
|
|
|
|
|
|
// 显示通知
|
|
|
|
|
|
this.visible = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 3秒后自动隐藏
|
|
|
|
|
|
this.autoHideTimer = setTimeout(() => {
|
|
|
|
|
|
this.hideNotification();
|
|
|
|
|
|
}, 3000);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 隐藏通知
|
|
|
|
|
|
*/
|
|
|
|
|
|
hideNotification() {
|
|
|
|
|
|
this.visible = false;
|
|
|
|
|
|
if (this.autoHideTimer) {
|
|
|
|
|
|
clearTimeout(this.autoHideTimer);
|
|
|
|
|
|
this.autoHideTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 点击通知,跳转到聊天页面
|
|
|
|
|
|
*/
|
|
|
|
|
|
handleClick() {
|
|
|
|
|
|
this.hideNotification();
|
|
|
|
|
|
|
|
|
|
|
|
if (this.roomId && this.roomData) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/subPackages/chat/chat?room_id=${this.roomId}&nick_name=${this.roomData.nick_name || ''}&user_id=${this.roomData.user_id || ''}&avatar=${encodeURIComponent(this.roomData.avatar || '')}`
|
|
|
|
|
|
});
|
|
|
|
|
|
} else if (this.roomId) {
|
|
|
|
|
|
// 如果只有 roomId,也尝试跳转
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/subPackages/chat/chat?room_id=${this.roomId}`
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 关闭通知
|
|
|
|
|
|
*/
|
|
|
|
|
|
handleClose() {
|
|
|
|
|
|
this.hideNotification();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.notification-wrapper {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
padding-left: 20rpx;
|
|
|
|
|
|
padding-right: 20rpx;
|
|
|
|
|
|
padding-bottom: 20rpx;
|
|
|
|
|
|
// padding-top 通过 :style 动态设置,避开胶囊按钮
|
|
|
|
|
|
transform: translateY(-100%);
|
|
|
|
|
|
transition: transform 0.3s ease-out;
|
|
|
|
|
|
|
|
|
|
|
|
&.slide-in {
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.notification {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
|
|
|
|
|
|
.avatar {
|
|
|
|
|
|
width: 80rpx;
|
|
|
|
|
|
height: 80rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-left: 20rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #666666;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.close {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
width: 48rpx;
|
|
|
|
|
|
height: 48rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-left: 16rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.close-icon {
|
|
|
|
|
|
font-size: 40rpx;
|
|
|
|
|
|
color: #999999;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|