Files
jiapuapp/pages/notification/n01-message-center.vue
T
2026-07-23 08:24:07 +08:00

261 lines
6.7 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.
<!-- 页面编号N-01用途消息中心已读操作空态与失败状态 -->
<template>
<view
class="notice-page"
:class="{
'notice-state--loading': noticeState === 'loading',
'notice-state--list': noticeState === 'list',
'notice-state--empty': noticeState === 'empty',
'notice-state--error': noticeState === 'error',
}"
>
<ModulePageBackground module="notification" />
<view class="notice-page__header">
<PageHeader
title="消息中心"
:action="unreadCount > 0 && noticeState === 'list' ? '全部已读' : ''"
@action="markAllRead"
/>
</view>
<view class="notice-content">
<AppLoading
v-if="noticeState === 'loading'"
text="正在整理消息"
description="请稍候,正在同步家谱申请与家族提醒。"
/>
<template v-else-if="noticeState === 'list'">
<view class="notice-list">
<view
v-for="item in notices"
:key="item.id"
class="notice-card"
role="button"
:aria-label="`${item.unread ? '未读' : '已读'}消息${item.title}`"
@click="openNotice(item)"
>
<view class="notice-card__copy">
<text
class="notice-card__status"
:class="{ 'is-unread': item.unread }"
>{{ item.unread ? "未读提醒" : "已读" }} · {{ item.time }}</text
>
<text class="notice-card__title">{{ item.title }}</text>
<text class="notice-card__summary">{{ item.content }}</text>
</view>
</view>
</view>
<AppButton
class="notice-review-action"
block
type="secondary"
label="前往入谱审核"
@click="toReview"
/>
</template>
<view v-else class="notice-state-card">
<view class="notice-state-card__copy">
<text class="notice-state-card__title">{{
noticeState === "empty" ? "暂时没有新消息" : "消息中心暂不可用"
}}</text>
<text class="notice-state-card__description">{{
noticeState === "empty"
? "家谱申请、审核结果和家族提醒会留在这里。"
: "请稍后重新进入,已读状态不会受到影响。"
}}</text>
</view>
<AppButton
block
:type="noticeState === 'error' ? 'secondary' : 'primary'"
:label="noticeState === 'error' ? '重新查看' : '前往入谱审核'"
@click="noticeState === 'error' ? restoreList() : toReview()"
/>
</view>
</view>
<AppToast :visible="toastVisible" :message="toastMessage" />
</view>
</template>
<script setup>
import { computed, onUnmounted, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { listNotificationFixtures } from "@/data/mock.js";
import { genealogyContext } from "@/utils/genealogy-context.js";
import { openPage } from "@/utils/navigation.js";
const genealogyId = ref("");
const noticeState = ref("loading");
const toastVisible = ref(false);
const toastMessage = ref("");
let toastTimer = null;
const notices = ref(listNotificationFixtures());
onLoad((query) => {
genealogyId.value =
query.genealogyId || genealogyContext.getCurrentGenealogyId() || "";
noticeState.value =
query.state === "loading"
? "loading"
: query.state === "empty"
? "empty"
: query.state === "error"
? "error"
: "list";
});
const unreadCount = computed(
() => notices.value.filter((item) => item.unread).length,
);
const openNotice = async (item) => {
const opened = await openPage(
"N02",
{ id: String(item.id) },
"N01",
);
if (opened) item.unread = false;
};
const restoreList = () => {
noticeState.value = "list";
};
const showToast = (message) => {
toastMessage.value = message;
toastVisible.value = true;
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toastVisible.value = false;
toastTimer = null;
}, 1800);
};
const markAllRead = () => {
notices.value.forEach((item) => {
item.unread = false;
});
showToast("已全部标记为已读");
};
const toReview = () =>
genealogyId.value
? openPage(
"G10",
{ genealogyId: genealogyId.value },
"N01",
)
: showToast("请先选择可管理的家谱");
onUnmounted(() => {
if (toastTimer) clearTimeout(toastTimer);
});
</script>
<style scoped lang="scss">
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
.notice-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.notice-page__header,
.notice-content {
z-index: 1;
}
.notice-content {
flex: 1;
padding: 24rpx 28rpx 100rpx;
}
.notice-list {
display: flex;
flex-direction: column;
gap: 18rpx;
}
.notice-card {
@include adaptive.adaptive-notification-content;
width: 100%;
min-height: 220rpx;
}
.notice-card__copy {
display: flex;
min-height: 220rpx;
box-sizing: border-box;
flex-direction: column;
justify-content: center;
padding: 30rpx 44rpx;
}
.notice-card__status {
display: block;
color: $ink-muted;
font-size: 21rpx;
font-weight: 600;
letter-spacing: 2rpx;
}
.notice-card__status.is-unread {
color: $brand-red;
font-weight: 700;
}
.notice-card__title {
display: block;
margin-top: 7rpx;
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 30rpx;
font-weight: 700;
}
.notice-card__summary {
display: block;
margin-top: 7rpx;
color: #62584c;
font-size: 23rpx;
font-weight: 500;
line-height: 1.45;
}
.notice-review-action {
margin: 30rpx auto 0;
}
.notice-state-card {
margin-top: 38rpx;
background: url("/static/assets/modules/notification/transparent/n01-notice-card.png") top center / 100% 220rpx no-repeat;
text-align: center;
}
.notice-state-card__copy {
display: flex;
min-height: 118px;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 54rpx;
}
.notice-state-card__title {
display: block;
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 31rpx;
font-weight: 700;
}
.notice-state-card__description {
display: block;
margin-top: 13rpx;
color: $ink-muted;
font-size: 23rpx;
line-height: 1.5;
}
.notice-state-card .app-button {
margin: 20rpx auto 0;
}
@media (min-width: 400px) {
.notice-content {
padding-right: 32rpx;
padding-left: 32rpx;
}
}
</style>