完成70%

This commit is contained in:
2026-07-24 07:56:22 +08:00
parent bb6431b319
commit c7f278fe79
92 changed files with 4741 additions and 14440 deletions
+5 -253
View File
@@ -1,260 +1,12 @@
<!-- 页面编号N-01用途消息中心已读操作空态与失败状态 -->
<!-- 页面编号N-01用途通知入口列表 item DTO 缺失时不展示 fixture 通知 -->
<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>
<view class="message-page"><ModulePageBackground module="notification" /><view class="page-header"><PageHeader root title="消息中心" /></view><view class="page-content"><view class="state-card"><text>消息列表待后端字段合同</text><text>通知列表没有声明通知 ID已读状态标题时间正文类型或跳转参数页面已停止展示本地消息也不会以 fixture 修改已读状态</text><AppButton block label="返回我的" @click="returnToProfile" /></view></view><AppTabbar active="profile" /></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);
});
import AppButton from "@/components/AppButton.vue"; import AppTabbar from "@/components/AppTabbar.vue"; import ModulePageBackground from "@/components/ModulePageBackground.vue"; import PageHeader from "@/components/PageHeader.vue"; import { goRoot } from "@/utils/navigation.js";
const returnToProfile=()=>goRoot("M01");
</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;
}
}
.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 190rpx}.state-card{box-sizing:border-box;min-height:340rpx;padding:78rpx 52rpx 50rpx;text-align:center;@include adaptive.adaptive-family-content}.state-card text{display:block}.state-card text:first-child{color:$ink;font-family:STKaiti,KaiTi,serif;font-size:35rpx;font-weight:700}.state-card text:nth-child(2){margin-top:18rpx;color:$ink-muted;font-size:24rpx;line-height:1.65}.state-card .app-button{margin-top:28rpx}
</style>