topfans/frontend/pages/mailbox/composables/useMailboxCenter.js
2026-07-14 21:59:15 +08:00

121 lines
4.1 KiB
JavaScript

import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import {
getNotificationsApi, markAsReadApi, markAllAsReadApi,
deleteNotificationApi, clearNotificationsApi
} from '@/utils/api.js'
const GROUPS = [
{ type: 'activity', label: '活动通知' },
{ type: 'feedback_replied', label: '反馈回复' },
{ type: 'report_resolved', label: '举报结果' },
{ type: 'target_reported', label: '被举报' },
]
export function useMailboxCenter() {
const store = useStore()
const itemsByType = computed(() => store.state.mailbox.itemsByType)
const unreadByType = computed(() => store.state.mailbox.unreadByType)
const loading = computed(() => store.state.mailbox.loading)
const hasAny = computed(() => GROUPS.some(g => (itemsByType.value[g.type] || []).length > 0))
const hasMore = computed(() => Object.values(store.state.mailbox.noMoreByType).some(v => !v))
const collapsedMap = ref({})
collapsedMap.value = (() => {
try { return uni.getStorageSync('mailbox_collapsed') || {} } catch (e) { return {} }
})()
// 列表页 onShow 时并发 4 个 type 各拉首页
async function loadAll() {
store.commit('mailbox/SET_LOADING', true)
try {
await Promise.all(GROUPS.map(g =>
getNotificationsApi({ type: g.type, page: 1, page_size: 20 })
.then((resp) => {
const items = resp.data?.items || []
store.commit('mailbox/SET_ITEMS', { type: g.type, items, sortUnreadFirst: true })
store.commit('mailbox/SET_NO_MORE', { type: g.type, value: items.length < 20 })
if (resp.data?.unreadByType) store.commit('mailbox/REPLACE_UNREAD_BY_TYPE', resp.data.unreadByType)
})
))
} finally {
store.commit('mailbox/SET_LOADING', false)
}
}
function onScrollLower() {
const target = GROUPS.find(g => !store.state.mailbox.noMoreByType[g.type])
if (target) loadMore(target.type)
}
async function loadMore(type) {
const page = Math.floor((itemsByType.value[type].length / 20) + 1)
const resp = await getNotificationsApi({ type, page, page_size: 20 })
const items = resp.data?.items || []
store.commit('mailbox/APPEND_ITEMS', { type, items })
store.commit('mailbox/SET_NO_MORE', { type, value: items.length < 20 })
}
async function onMarkRead(nid, type) {
await markAsReadApi(nid)
store.commit('mailbox/UPDATE_ITEM', { type, id: nid, patch: { is_read: true } })
store.commit('mailbox/DECREMENT_TYPE', type)
store.commit('mailbox/RESORT_ITEMS', type) // 已读排后
}
async function onMarkAllRead() {
await Promise.all(GROUPS.map(g => markAllAsReadApi(g.type)))
store.commit('mailbox/MARK_ALL_READ')
}
async function onDeleteOne(nid, type) {
await deleteNotificationApi(nid)
store.commit('mailbox/REMOVE_ITEM', { type, id: nid })
}
async function onClearAll() {
const ok = await new Promise((resolve) => {
uni.showModal({ title: '提示', content: '清空所有通知?', success: (r) => resolve(r.confirm) })
})
if (!ok) return
await clearNotificationsApi('all')
store.commit('mailbox/CLEAR_ALL')
}
function onSelectItem(n) {
if (!n || !n.id) return
onMarkRead(n.id, n.type)
uni.navigateTo({ url: `/pages/mailbox/detail?nid=${n.id}&type=${n.type}` })
}
function onLongPress(n) {
if (!n || !n.id) return
uni.showActionSheet({
itemList: ['标已读', '删除'],
success: (res) => {
if (res.tapIndex === 0) onMarkRead(n.id, n.type)
else if (res.tapIndex === 1) onDeleteOne(n.id, n.type)
}
})
}
function isCollapsed(type) {
return !!collapsedMap.value[type]
}
function toggleCollapse(type) {
const next = { ...collapsedMap.value, [type]: !collapsedMap.value[type] }
collapsedMap.value = next
try { uni.setStorageSync('mailbox_collapsed', next) } catch (e) {}
}
return {
GROUPS,
itemsByType, unreadByType, loading, hasAny, hasMore,
loadAll, onScrollLower, loadMore,
onMarkRead, onMarkAllRead, onDeleteOne, onClearAll,
onSelectItem, onLongPress,
isCollapsed, toggleCollapse,
}
}