diff --git a/miniprogram/pages/notifications/notifications.ts b/miniprogram/pages/notifications/notifications.ts index 10525d6..337e295 100644 --- a/miniprogram/pages/notifications/notifications.ts +++ b/miniprogram/pages/notifications/notifications.ts @@ -1,201 +1,136 @@ -// 定义通知数据类型 -interface Notification { - id: string; - type: 'system' | 'activity' | 'message'; - title: string; - message: string; - time: string; - read: boolean; -} +// message.ts (适配最新数据库结构) -Page({ - data: { - // 标签状态 - currentTab: 0, - - // 所有通知数据 - notifications: [] as Notification[], - - // 过滤后的通知(根据当前标签) - filteredNotifications: [] as Notification[], - - // 未读数量统计 - totalUnread: 0, - systemUnread: 0, - activityUnread: 0, - messageUnread: 0 - }, - - onLoad() { - // 初始化通知数据 - this.initNotifications(); - }, - - // 初始化通知数据 - initNotifications() { - // 模拟通知数据 - const notifications: Notification[] = [ - { - id: '1', - type: 'system', - title: '系统更新通知', - message: '您的应用已更新至最新版本,新增多项功能,提升了使用体验。', - time: '今天 08:30', - read: false - }, - { - id: '2', - type: 'activity', - title: '新活动上线', - message: '限时优惠活动已开始,点击查看详情,参与活动赢取大奖!', - time: '昨天 15:45', - read: false - }, - { - id: '3', - type: 'message', - title: '张三给你发了消息', - message: '在吗?上次说的事情有进展了,我们约个时间讨论一下吧。', - time: '昨天 10:20', - read: true - }, - { - id: '4', - type: 'system', - title: '账号安全提醒', - message: '您的账号在新设备登录,如非本人操作,请及时修改密码。', - time: '2023-05-15', - read: false - }, - { - id: '5', - type: 'activity', - title: '活动即将结束', - message: '您参与的积分兑换活动还有3天结束,抓紧时间兑换哦!', - time: '2023-05-14', - read: true - }, - { - id: '6', - type: 'message', - title: '李四回复了你的评论', - message: '你说得很有道理,我也这么认为。', - time: '2023-05-12', - read: false - } - ]; - - // 更新数据 - this.setData({ - notifications - }); - - // 过滤通知并计算未读数量 - this.filterNotifications(); - this.calculateUnreadCounts(); - }, - - // 切换标签 - switchTab(e: { currentTarget: { dataset: { tab: number } } }) { - const tab = e.currentTarget.dataset.tab; - this.setData({ - currentTab: tab - }); - this.filterNotifications(); - }, - - // 根据当前标签过滤通知 - filterNotifications() { - const { currentTab, notifications } = this.data; - let filtered: Notification[] = []; - - switch(currentTab) { - case 0: // 全部 - filtered = notifications; - break; - case 1: // 系统 - filtered = notifications.filter(item => item.type === 'system'); - break; - case 2: // 活动 - filtered = notifications.filter(item => item.type === 'activity'); - break; - case 3: // 私信 - filtered = notifications.filter(item => item.type === 'message'); - break; - } - - this.setData({ - filteredNotifications: filtered - }); - }, - - // 计算未读数量 - calculateUnreadCounts() { - const { notifications } = this.data; - - // 计算各类未读数量 - const systemUnread = notifications.filter(item => item.type === 'system' && !item.read).length; - const activityUnread = notifications.filter(item => item.type === 'activity' && !item.read).length; - const messageUnread = notifications.filter(item => item.type === 'message' && !item.read).length; - const totalUnread = systemUnread + activityUnread + messageUnread; - - this.setData({ - totalUnread, - systemUnread, - activityUnread, - messageUnread - }); - }, - - // 打开通知详情 - openNotification(e: { currentTarget: { dataset: { id: string } } }) { - const id = e.currentTarget.dataset.id; - const { notifications } = this.data; - - // 将通知标记为已读 - const updatedNotifications = notifications.map(item => { - if (item.id === id && !item.read) { - return { ...item, read: true }; - } - return item; - }); - - this.setData({ - notifications: updatedNotifications - }); - - // 更新过滤列表和未读数量 - this.filterNotifications(); - this.calculateUnreadCounts(); - - // 跳转到通知详情页(实际项目中实现) - wx.showToast({ - title: '查看通知详情', - icon: 'none' - }); - }, - - // 标记全部已读 - markAllAsRead() { - const { notifications } = this.data; - - // 将所有通知标记为已读 - const updatedNotifications = notifications.map(item => ({ - ...item, - read: true - })); - - this.setData({ - notifications: updatedNotifications - }); - - // 更新过滤列表和未读数量 - this.filterNotifications(); - this.calculateUnreadCounts(); - - wx.showToast({ - title: '全部已读', - icon: 'none' - }); +// 接口:定义从API获取的原始消息数据结构 (与数据库完全一致) +interface IApiMessage { + id: number; + sender_id: string; + receiver_id: string; + status: number; // 假设 status 字段仍有其他用途 + sequence: string; + created_at: string; // e.g., "2025-09-28 18:10:00" + content: string; + is_read: 0 | 1; // 0: 未读, 1: 已读 + msg_type: 'comment' | 'like' | 'follow' | 'system'; + target: string | null; // e.g., "post:123", "user:456", "/pages/...", or null } -}); + + // 接口:定义页面渲染所需的数据结构 + interface IViewMessage { + id: number; + type: 'system' | 'user'; // 用于区分图标 + title: string; + content: string; + timestamp: string; + is_read: 0 | 1; // 用于显示未读红点 + target: string | null; // 用于点击跳转 + } + + Page({ + data: { + systemIcon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzQ3OTZGMyI+PHBhdGggZD0iTTIgMTVoMnYyaC0yem0wLTRoMnYyaC0yem0wLThoMnY2aC0yem00IDEyaDJ2MmgtMnptMC00aDJ2MmgtMnptMC00aDJ2MmgtMnptMTYtNGgtOHYtMmgtNHYyaC0yYTEgMSAwIDAgMC0xIDF2MTBhMSAxIDAgMCAwIDEgMWgxNGExIDEgMCAwIDAgMS0xdi0xMGExIDEgMCAwIDAtMS0xem0tMyAxMGgtMTB2LThoMTB6bS0xMS00aDJ2MmgtMnptMC00aDJ2MmgtMnptNCA0aDJ2MmgtMnptMC00aDJ2MmgtMnptNCA0aDJ2MmgtMnptMC00aDJ2MmgtMnoiLz48L3N2Zz4=', + userIcon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZDQjMwMCI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMSu3OS00IDQgMS.3OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAyLjY5LTggNnYxaDE2di0xYzAtMi4zMS01LjMzLTUtOC01eiIvPjwvc3ZnPg==', + + messageList: [] as IViewMessage[], + }, + + onLoad() { + this.fetchAndProcessMessages(); + }, + + fetchAndProcessMessages() { + // 1. 模拟从您的后端API获取的、符合最新数据库结构的原始数据 + const apiResponse: IApiMessage[] = [ + { id: 101, sender_id: "user_1001", receiver_id: "me", msg_type: "comment", status: 1, sequence: "seq_001", created_at: "2025-09-28 15:30:00", content: "这张照片的构图太棒了!", is_read: 0, target: "post:abc-123" }, + { id: 102, sender_id: "user_2035", receiver_id: "me", msg_type: "like", status: 1, sequence: "seq_002", created_at: "2025-09-28 09:15:00", content: "你的帖子「周末徒步之旅」", is_read: 0, target: "post:def-456" }, + { id: 103, sender_id: "user_3110", receiver_id: "me", msg_type: "follow", status: 1, sequence: "seq_003", created_at: "2025-09-27 20:05:00", content: "", is_read: 1, target: "user:user-3110" }, + { id: 104, sender_id: "system", receiver_id: "me", msg_type: "system", status: 1, sequence: "seq_004", created_at: "2025-09-26 10:00:00", content: "恭喜你获得了「创作新星」徽章,再接再厉!", is_read: 1, target: "/pages/my-badges/index" }, + { id: 105, sender_id: "user_1001", receiver_id: "me", msg_type: "like", status: 1, sequence: "seq_005", created_at: "2025-09-25 11:45:00", content: "你的帖子「美食探店分享」", is_read: 1, target: "post:ghi-789" }, + ]; + + const viewList = apiResponse.map(msg => this.transformMessage(msg)); + + this.setData({ + messageList: viewList + }); + }, + + transformMessage(msg: IApiMessage): IViewMessage { + let title = ''; + let content = ''; + const senderName = msg.sender_id; + + // ⭐️ 现在根据 msg_type 判断 + switch (msg.msg_type) { + case 'comment': title = '新的评论'; content = `「${senderName}」评论了你:「${msg.content}」`; break; + case 'like': title = '新的点赞'; content = `「${senderName}」赞了${msg.content}`; break; + case 'follow': title = '新的关注'; content = `「${senderName}」关注了你`; break; + case 'system': title = '系统通知'; content = msg.content; break; + } + + return { + // ⭐️ 使用数据库的自增 id + id: msg.id, + // ⭐️ type 用于判断图标 + type: msg.msg_type === 'system' ? 'system' : 'user', + title: title, + content: content, + timestamp: this.formatDisplayTime(msg.created_at), + // ⭐️ is_read 用于判断未读红点 + is_read: msg.is_read, + // ⭐️ target 用于点击跳转 + target: msg.target, + }; + }, + + // ⭐️ 新增/恢复:卡片点击跳转的处理函数 + handleNavigate(event: WechatMiniprogram.TouchEvent) { + const { target } = event.currentTarget.dataset; + + if (!target) { + console.log("此消息没有可跳转的目标"); + return; + } + + let url = ''; + // 解析 target, e.g., "post:abc-123" + const parts = target.split(':'); + const type = parts[0]; + const id = parts[1]; + + switch (type) { + case 'post': + url = `/pages/post-detail/index?id=${id}`; + break; + case 'user': + url = `/pages/user-profile/index?userId=${id}`; + break; + default: + // 如果 target 不包含 ':', 说明它可能是一个完整的内部页面路径 + if (target.startsWith('/')) { + url = target; + } else { + console.warn("未知的 target 类型:", target); + return; + } + } + + wx.navigateTo({ + url: url, + fail: (err) => { + console.error("页面跳转失败", err); + wx.showToast({ title: '页面不存在', icon: 'none' }); + } + }); + }, + + formatDisplayTime(dateString: string): string { + const date = new Date(dateString.replace(/-/g, '/')); + const now = new Date(); + const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const yesterdayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); + const time = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; + if (date.getTime() >= todayStart.getTime()) return `今天 ${time}`; + if (date.getTime() >= yesterdayStart.getTime()) return `昨天 ${time}`; + return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; + } + }); \ No newline at end of file diff --git a/miniprogram/pages/notifications/notifications.wxml b/miniprogram/pages/notifications/notifications.wxml index e92f87f..2f85956 100644 --- a/miniprogram/pages/notifications/notifications.wxml +++ b/miniprogram/pages/notifications/notifications.wxml @@ -1,54 +1,35 @@ - - - - 消息通知 - - 全部已读 - - - - - - - 全部 - {{totalUnread}} - - - 系统 - {{systemUnread}} - - - 活动 - {{activityUnread}} - - - 私信 - {{messageUnread}} - - - - - - - - - {{item.type === 'system' ? '系' : item.type === 'activity' ? '活' : '私'}} + + + + + + + {{ item.title }} + - + {{ item.timestamp }} - - - {{item.title}} - {{item.time}} - - {{item.message}} + + + {{ item.content }} + + + + + + 查看详情 + - - - - 📭 - 暂无通知 - + + + + + 暂无新消息 - + \ No newline at end of file diff --git a/miniprogram/pages/notifications/notifications.wxss b/miniprogram/pages/notifications/notifications.wxss index 46ec7bd..c14a517 100644 --- a/miniprogram/pages/notifications/notifications.wxss +++ b/miniprogram/pages/notifications/notifications.wxss @@ -1,181 +1,127 @@ -.notification-page { - background-color: #f5f5f5; - min-height: 100vh; -} - -/* 导航栏样式 */ -.navbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px; - background-color: white; - border-bottom: 1px solid #eee; -} - -.navbar-title { - font-size: 18px; - font-weight: 600; - color: #333; -} - -.mark-read { - font-size: 14px; - color: #07c160; -} - -/* 标签样式 */ -.tabs { - display: flex; - background-color: white; - border-bottom: 1px solid #eee; - overflow-x: auto; - white-space: nowrap; -} - -.tab-item { - display: inline-flex; - align-items: center; - padding: 14px 20px; - font-size: 15px; - color: #666; - position: relative; -} - -.tab-item.active { - color: #07c160; -} - -.tab-item.active::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 2px; - background-color: #07c160; -} - -.badge { - display: flex; - align-items: center; - justify-content: center; - width: 18px; - height: 18px; - border-radius: 50%; - background-color: #ff3b30; - color: white; - font-size: 12px; - margin-left: 6px; -} - -/* 通知列表样式 */ -.notification-list { - padding-bottom: 20px; -} - -.notification-item { - display: flex; - padding: 16px; - background-color: white; - border-bottom: 1px solid #eee; - transition: background-color 0.2s; -} - -.notification-item:active { - background-color: #f9f9f9; -} - -.avatar-container { - position: relative; - margin-right: 14px; -} - -.avatar { - width: 40px; - height: 40px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 16px; - font-weight: 500; -} - -.system-avatar { - background-color: #07c160; -} - -.activity-avatar { - background-color: #ff9500; -} - -.message-avatar { - background-color: #007aff; -} - -.unread-dot { - position: absolute; - top: -2px; - right: -2px; - width: 12px; - height: 12px; - border-radius: 50%; - background-color: #ff3b30; -} - -.notification-content { - flex: 1; - overflow: hidden; -} - -.notification-header { - display: flex; - justify-content: space-between; - margin-bottom: 4px; -} - -.title { - font-size: 16px; - color: #333; - font-weight: 500; - max-width: 80%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.time { - font-size: 12px; - color: #999; -} - -.message { - font-size: 14px; - color: #666; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - line-height: 1.4; -} - -/* 空状态样式 */ -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 60px 0; - color: #999; -} - -.empty-icon { - font-size: 60px; - margin-bottom: 20px; -} - -.empty-text { - font-size: 16px; -} +/* message.wxss */ +page { + background-color: #f4f6f8; + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif; + } + + .container { + padding: 32rpx; + } + + /* 卡片样式 */ + .card { + background-color: #ffffff; + border-radius: 20rpx; + margin-bottom: 32rpx; + padding: 32rpx; + box-shadow: 0 4rpx T12rpx rgba(0, 0, 0, 0.08); + transition: all 0.2s ease-in-out; + } + + .card:last-child { + margin-bottom: 0; + } + + /* 卡片头部 */ + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; + } + + .card-title-box { + display: flex; + align-items: center; + } + + .card-icon { + width: 48rpx; + height: 48rpx; + margin-right: 20rpx; + } + + .card-title { + font-size: 32rpx; + font-weight: 600; + color: #333333; + } + + .card-time { + font-size: 24rpx; + color: #999999; + } + + /* 卡片内容 */ + .card-body { + padding: 16rpx 0; + } + + .card-content { + font-size: 28rpx; + color: #555555; + line-height: 1.6; + /* 最多显示两行,超出部分显示省略号 */ + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + /* 分割线 */ + .divider { + height: 1rpx; + background-color: #e9e9e9; + margin-top: 24rpx; + } + + /* 卡片脚部 */ + .card-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 24rpx; + font-size: 28rpx; + color: #576b95; /* 微信主题链接色 */ + } + + .arrow { + font-size: 36rpx; + color: #cccccc; + } + + /* 点击态样式 */ + .cell-hover { + background-color: #f7f7f7; + } + + /* 无消息提示 */ + .no-message { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding-top: 200rpx; + color: #999; + } + + .no-message-icon { + width: 180rpx; + height: 180rpx; + margin-bottom: 20rpx; + } + + /* --- 新增样式 --- */ + /* 未读红点样式 */ + .unread-dot { + width: 16rpx; + height: 16rpx; + background-color: #fa5151; /* 微信主题红色 */ + border-radius: 50%; + margin-left: 12rpx; + } + .card-hover { + transform: scale(0.99); + box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1); + } \ No newline at end of file