import { request, redirectToLogin } from '../../utils/request' import * as auth from '../../utils/auth' import { PagedResult, NotificationItem } from '../../types/models' import { STORAGE_KEYS } from '../../utils/config' import { formatRelativeTime } from '../../utils/format' import { normalizePaged, mergePaged, hasMorePaged } from '../../utils/paged' import { getTabBarInstance } from '../../utils/tabbar' import { navScroll } from '../../utils/nav' import { logger } from '../../utils/logger' // 日志模块名 const LOG = 'notification' const FILTERS = [ { name: '全部', value: 0 }, { name: '点赞', value: 1 }, { name: '订单', value: 2 }, { name: '系统', value: 3 }, ] Page({ data: { filters: FILTERS, activeFilter: 0, list: [] as NotificationItem[], loading: true, loadingMore: false, page: 1, pageSize: 20, hasMore: true, // 滚动收缩:由 utils/nav.ts 的共享 handler 驱动 navScrolled: false, }, // scroll-view 滚动 → 导航收缩为玻璃小标题 onNavScroll: navScroll, onShow() { // 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求 if (!auth.isLoggedIn()) { logger.debug(LOG, '未登录,跳转登录页') redirectToLogin() return } // 先拉列表、再全部标记已读:本次进入仍保留未读标记,让用户看清哪些是新消息; // 服务端状态与红点在列表渲染后清零,下次进入即为已读样式。 this.refresh().then(() => this.markAllRead()) }, // 全部标记已读:接口顺带返回最新未读数,直接用它刷新红点,省掉一次 /notification/unread markAllRead(): Promise { return request<{ unread_count: number }>({ url: '/notification/read-all', method: 'POST', showError: false, silentAuth: true, }) .then((res) => { const unread = res.unread_count || 0 logger.debug(LOG, '全部标记已读成功, unread=', unread) this.syncUnread(unread) }) .catch((e: any) => { // 失败不阻塞列表展示,红点维持原状,等下次进入再补 logger.warn(LOG, '全部标记已读失败(静默降级), code=', e && e.code) }) }, // 同步未读红点:本页不是 tab 页,getTabBar() 恒为 undefined,需从页面栈里取 tab 页的 tabBar syncUnread(unread: number) { wx.setStorageSync(STORAGE_KEYS.UNREAD, unread) const tabBar = getTabBarInstance() // 直接更新红点,避免 tabBar 内部再发一次请求 if (tabBar && tabBar.setUnread) tabBar.setUnread(unread) }, // 返回列表请求的 Promise:onShow 需要等首页数据渲染完再标记全部已读 refresh(): Promise { this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 }) return this.fetchList(1) }, onReachBottom() { if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return this.fetchList(this.data.page + 1) }, fetchList(page: number): Promise { const params: Record = { page, page_size: this.data.pageSize } const filter = this.data.activeFilter if (filter > 0) params.msg_type = filter // 请求序号:切换筛选时旧响应可能后到,序号不匹配直接丢弃 const seq = ((this as any).reqSeq || 0) + 1 ;(this as any).reqSeq = seq if (page > 1) this.setData({ loadingMore: true }) return request>({ url: '/notification', data: params }) .then((res) => { if ((this as any).reqSeq !== seq) return const { list: raw, total } = normalizePaged(res) logger.debug(LOG, '通知加载成功, 筛选=', filter, 'page=', page, '条数=', raw && raw.length) const list = raw.map((it) => ({ ...it, _time: formatRelativeTime(it.created_at) })) const merged = mergePaged(this.data.list, list, page === 1) this.setData({ list: merged, hasMore: hasMorePaged(merged, total, this.data.pageSize), loading: false, loadingMore: false, page, }) }) .catch((e: any) => { if ((this as any).reqSeq !== seq) return logger.warn(LOG, '通知加载失败, page=', page, 'code=', e && e.code) this.setData({ loading: false, loadingMore: false }) }) }, onFilterTap(e: WechatMiniprogram.TouchEvent) { const index = Number(e.currentTarget.dataset.index) if (index === this.data.activeFilter) return this.setData({ activeFilter: index, list: [], loading: true }) this.refresh() }, onItemTap(e: WechatMiniprogram.TouchEvent) { const uuid = e.currentTarget.dataset.uuid as string wx.navigateTo({ url: `/pages/notification-detail/notification-detail?uuid=${uuid}`, fail: (e) => logger.warn(LOG, 'navigateTo 通知详情失败, err=', e && e.errMsg), }) }, })