import { useCallback, useState } from 'react' import Taro, { useDidShow } from '@tarojs/taro' import { View, Text } from '@tarojs/components' import { Cell, CellGroup, Empty, InfiniteLoading, PullToRefresh, Tag, Button } from '@nutui/nutui-react-taro' import { useThemeStyles } from '@/hooks/useTheme' import { pageNotification, getUnreadCount, markNotificationRead, markAllNotificationRead } from '@/api/app/notification' import type { Notification } from '@/api/app/notification/model' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import 'dayjs/locale/zh-cn' dayjs.extend(relativeTime) dayjs.locale('zh-cn') /** 通知类型配置 */ const TYPE_CONFIG: Record = { system: { label: '系统', color: '#3b82f6' }, ticket: { label: '工单', color: '#f59e0b' }, review: { label: '审核', color: '#8b5cf6' }, resource: { label: '资源', color: '#10b981' }, permission: { label: '权限', color: '#ef4444' }, member: { label: '成员', color: '#06b6d4' }, payment: { label: '支付', color: '#ec4899' }, } const NotificationPage = () => { const themeStyles = useThemeStyles() const [list, setList] = useState([]) const [loading, setLoading] = useState(false) const [hasMore, setHasMore] = useState(true) const [page, setPage] = useState(1) const [unreadTotal, setUnreadTotal] = useState(0) const loadNotifications = useCallback(async (isRefresh = false) => { if (loading) return setLoading(true) try { const currentPage = isRefresh ? 1 : page const res = await pageNotification({ page: currentPage, limit: 20 }) const newList = res?.list || [] if (isRefresh) { setList(newList) setPage(2) } else { setList(prev => [...prev, ...newList]) setPage(prev => prev + 1) } setHasMore(newList.length >= 20) } catch (e) { console.error('加载通知失败', e) } finally { setLoading(false) } }, [loading, page]) const loadUnreadCount = useCallback(async () => { try { const res = await getUnreadCount() setUnreadTotal(res?.total || 0) } catch { // ignore } }, []) useDidShow(() => { loadNotifications(true) loadUnreadCount() }) const handleMarkRead = async (item: Notification) => { if (item.isRead === 1 || !item.id) return try { await markNotificationRead(item.id) setList(prev => prev.map(n => n.id === item.id ? { ...n, isRead: 1 } : n)) loadUnreadCount() } catch (e) { console.error('标记已读失败', e) } } const handleMarkAllRead = async () => { try { await markAllNotificationRead() setList(prev => prev.map(n => ({ ...n, isRead: 1 }))) setUnreadTotal(0) Taro.showToast({ title: '已全部标记为已读', icon: 'success' }) } catch (e) { console.error('标记全部已读失败', e) } } const handleClick = (item: Notification) => { handleMarkRead(item) if (item.linkUrl) { // 外链通过 WebView 打开 Taro.navigateTo({ url: `/passport/webview/index?url=${encodeURIComponent(item.linkUrl)}` }) } } const formatTime = (time?: string) => { if (!time) return '' return dayjs(time).fromNow() } if (list.length === 0 && !loading) { return ( 消息通知 ) } return ( {/* 头部 */} 消息通知 {unreadTotal > 0 && ( {unreadTotal} 条未读 )} {unreadTotal > 0 && ( )} loadNotifications(true)}> {/* 通知列表 */} {list.map((item) => { const typeConf = TYPE_CONFIG[item.type || ''] || TYPE_CONFIG.system return ( handleClick(item)} > {/* 未读标记 */} {item.isRead === 0 ? ( ) : ( )} {/* 内容 */} {typeConf.label} {item.title || '系统通知'} {item.content && ( {item.content} )} {formatTime(item.createTime)} ) })} {/* 加载更多 */} loadNotifications(false)} loading={loading} > {loading ? '加载中...' : hasMore ? '下拉加载更多' : '没有更多了'} ) } export default NotificationPage