Files
jiapuapp/pages/notification/message-center.vue
T

229 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="message-page">
<ModulePageBackground module="notification" />
<view class="page-header"><PageHeader :root="!sourceKey" :custom-back="Boolean(sourceKey)" title="消息中心" :action="unreadCount > 0 ? '设为已读' : ''" @back="returnToSource" @action="requestMarkAllRead" /></view>
<view class="page-content">
<view v-if="notificationListState === 'loading'" class="state-card"><AppLoading text="正在读取消息通知" /></view>
<view v-else-if="notificationListState === 'error'" class="state-card">
<text>暂时无法读取消息状态</text>
<text>{{ notificationListError || "请检查网络后重新加载。" }}</text>
<AppButton block type="secondary" label="重新加载" @click="loadNotifications" />
<AppButton block :label="returnLabel" @click="returnToSource" />
</view>
<view v-else-if="!notifications.length" class="state-card message-status-card">
<text>当前没有通知</text>
<text>新的家谱动态审核和活动消息会在这里展示</text>
<text v-if="markAllReadError" class="message-operation-error">{{ markAllReadError }}</text>
<AppButton block type="secondary" :label="returnLabel" @click="returnToSource" />
</view>
<view v-else class="message-list">
<view
v-for="item in notifications"
:key="item.notificationId"
class="message-row"
hover-class="action-hover"
@click="openNotification(item)"
>
<view class="message-row__body">
<view class="message-row__title-line">
<text class="message-row__title">{{ item.noticeTitle || '通知消息' }}</text>
<text v-if="item.readStatus === '0'" class="message-row__unread">未读</text>
</view>
<text class="message-row__summary">{{ item.noticeContent || item.bizSummary || '暂无通知摘要' }}</text>
<text class="message-row__meta">{{ item.publishTime || '刚刚发布' }}</text>
</view>
<text class="message-row__chevron"></text>
</view>
<text v-if="markAllReadError" class="message-operation-error">{{ markAllReadError }}</text>
<AppButton v-if="unreadCount" block label="全部设为已读" @click="requestMarkAllRead" />
</view>
</view>
<AppPromotionStrip placement="message_bottom" title="消息页推荐" />
<AppTabbar :active="sourceTab" />
<AppDialog
:visible="markAllVisible"
eyebrow="消息状态"
title="把全部消息设为已读?"
message="这些消息将不再显示为未读。"
:confirm-text="markAllSubmitting ? '正在设置' : '确认设为已读'"
cancel-text="取消"
show-cancel
:close-on-mask="false"
@confirm="confirmMarkAllRead"
@cancel="markAllVisible = false"
/>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppDialog from "@/components/AppDialog.vue";
import AppLoading from "@/components/AppLoading.vue";
import AppPromotionStrip from "@/components/AppPromotionStrip.vue";
import AppTabbar from "@/components/AppTabbar.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import {
createRequestController,
isRequestCancelled
} from "@/services/api/request-controller.js";
import { notificationApi } from "@/services/api/notification-service.js";
import { getRequestErrorMessage } from "@/services/api/request-error-message.js";
import { goBack, goRoot, handleBackPress, openPage } from "@/utils/navigation/gateway.js";
const unreadCount = ref(0);
const notifications = ref([]);
const notificationListState = ref("loading");
const notificationListError = ref("");
const notificationListController = createRequestController();
const markAllReadController = createRequestController();
const markAllVisible = ref(false);
const markAllSubmitting = ref(false);
const markAllReadError = ref("");
const sourceKey = ref("");
let isPageActive = true;
const returnLabel = computed(() => (sourceKey.value ? "返回上一页" : "返回我的"));
const sourceTab = computed(() => (sourceKey.value === "G01" ? "genealogy" : "profile"));
const loadNotifications = async () => {
notificationListController.abort();
notificationListState.value = "loading";
notificationListError.value = "";
try {
const notificationRows = await notificationApi.getNotifications({
requestController: notificationListController,
});
if (!isPageActive) return;
notifications.value = notificationRows;
unreadCount.value = notificationRows.filter((item) => item.readStatus === "0").length;
notificationListState.value = "ready";
} catch (error) {
if (!isPageActive || isRequestCancelled(error)) return;
notificationListError.value = getRequestErrorMessage(error, "请稍后重试。");
notificationListState.value = "error";
}
};
const requestMarkAllRead = () => {
if (notificationListState.value !== "ready" || !unreadCount.value || markAllSubmitting.value) return;
markAllReadError.value = "";
markAllVisible.value = true;
};
const confirmMarkAllRead = async () => {
if (markAllSubmitting.value || !unreadCount.value) return;
markAllSubmitting.value = true;
markAllReadError.value = "";
try {
await notificationApi.markAllNotificationsRead({ requestController: markAllReadController });
if (!isPageActive) return;
markAllVisible.value = false;
await loadNotifications();
} catch (error) {
if (!isPageActive || isRequestCancelled(error)) return;
markAllReadError.value = getRequestErrorMessage(error, "全部标记已读失败,请稍后重试。");
} finally {
if (isPageActive) markAllSubmitting.value = false;
}
};
const returnToSource = () => (sourceKey.value ? goBack() : goRoot("M01"));
const closeMarkAllDialog = () => {
if (markAllSubmitting.value) return false;
markAllVisible.value = false;
return true;
};
const openNotification = (item) => openPage("N02", { id: item.notificationId });
onLoad((query) => {
sourceKey.value = String(query?.sourceKey || "");
loadNotifications();
});
onShow(() => {
if (notificationListState.value !== "loading") loadNotifications();
});
onBackPress((event) =>
markAllVisible.value ? handleBackPress(event, closeMarkAllDialog) : false,
);
onUnload(() => {
isPageActive = false;
notificationListController.abort();
markAllReadController.abort();
});
</script>
<style scoped lang="scss">
@import "../../styles/adaptive-frame-profiles.scss";
.message-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.page-header,
.page-content {
z-index: 1;
}
.page-content {
flex: 1;
padding: 18rpx 24rpx calc(190rpx + env(safe-area-inset-bottom));
}
.state-card {
box-sizing: border-box;
min-height: 340rpx;
padding: 78rpx 52rpx 50rpx;
text-align: center;
@include adaptive-notification-content;
}
.state-card text {
display: block;
}
.state-card text:first-child {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: clamp(19px, 35rpx, 24px);
font-weight: 700;
}
.state-card text:nth-child(2) {
margin-top: 18rpx;
color: $ink-muted;
font-size: clamp(15px, 24rpx, 18px);
line-height: 1.65;
}
.state-card .app-button {
margin-top: 28rpx;
}
.message-list {
display: grid;
gap: 20rpx;
box-sizing: border-box;
padding: 28rpx 24rpx;
@include adaptive-notification-content;
}
.message-row {
display: flex;
gap: 18rpx;
align-items: center;
padding: 24rpx;
border: 1rpx solid rgba(128, 89, 49, 0.26);
border-radius: 12rpx;
background: rgba($paper, 0.9);
}
.message-row__body { min-width: 0; flex: 1; }
.message-row__title-line { display: flex; gap: 12rpx; align-items: center; }
.message-row__title { overflow: hidden; flex: 1; color: $ink; font-size: clamp(16px, 27rpx, 20px); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
.message-row__unread { flex: 0 0 auto; color: $brand-red; font-size: clamp(12px, 20rpx, 15px); }
.message-row__summary, .message-row__meta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.message-row__summary { margin-top: 9rpx; color: $ink-muted; font-size: clamp(14px, 23rpx, 17px); }
.message-row__meta { margin-top: 9rpx; color: #8b7d6c; font-size: clamp(13px, 21rpx, 16px); }
.message-row__chevron { color: $ink-muted; font-size: clamp(24px, 42rpx, 30px); line-height: 1; }
.message-operation-error {
margin-top: 18rpx;
color: $brand-red;
font-size: clamp(14px, 22rpx, 17px);
line-height: 1.5;
}
</style>