144 lines
6.8 KiB
Vue
144 lines
6.8 KiB
Vue
<!-- 页面编号:T-08;用途:展示指定成员的隐私、纪念或无权限状态。 -->
|
||
<template>
|
||
<view
|
||
class="member-status-page"
|
||
:class="{
|
||
'member-status--privacy': statusState === 'privacy',
|
||
'member-status--deceased': statusState === 'deceased',
|
||
'member-status--forbidden': statusState === 'forbidden',
|
||
'member-status--error': statusState === 'error',
|
||
}"
|
||
>
|
||
<ModulePageBackground module="tree" />
|
||
<view class="member-status-page__header"><PageHeader :title="pageTitle" /></view>
|
||
|
||
<view v-if="hasValidContext" class="member-status-context">
|
||
<text>{{ genealogyName }}</text>
|
||
<text v-if="member">{{ memberIdentityCopy }}</text>
|
||
<text v-else>未找到成员身份</text>
|
||
</view>
|
||
|
||
<view class="status-card">
|
||
<view class="status-card__copy">
|
||
<text>{{ activeStatus.eyebrow }}</text>
|
||
<text>{{ activeStatus.title }}</text>
|
||
<text>{{ activeStatus.copy }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="status-guidance">
|
||
<view>
|
||
<text>{{ activeStatus.guideTitle }}</text>
|
||
<text v-for="line in activeStatus.guides" :key="line">{{ line }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="status-action" @click="handleAction">
|
||
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="scaleToFill" />
|
||
<text>{{ activeStatus.action }}</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from "vue";
|
||
import { onLoad } from "@dcloudio/uni-app";
|
||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||
import PageHeader from "@/components/PageHeader.vue";
|
||
import { findTreeMemberPresentationFixture } from "@/data/mock.js";
|
||
import { goBack, goRoot } from "@/utils/navigation.js";
|
||
|
||
const genealogyId = ref("");
|
||
const personId = ref("");
|
||
const statusState = ref("loading");
|
||
const genealogyName = ref("汤氏家谱");
|
||
const member = ref(null);
|
||
|
||
const states = {
|
||
privacy: {
|
||
eyebrow: "隐私成员",
|
||
title: "敏感资料只向授权成员展示",
|
||
copy: "该成员姓名和世系位置仍然保留,出生信息、联系方式和生平内容按权限隐藏。",
|
||
guideTitle: "当前可见范围",
|
||
guides: ["姓名、世代与家族关系可见", "联系方式和详细生平已隐藏", "本人或谱主可维护授权范围"],
|
||
action: "返回成员档案",
|
||
},
|
||
deceased: {
|
||
eyebrow: "离世纪念",
|
||
title: "这位家人的生命记录被温和保留",
|
||
copy: "离世状态不会删除成员关系;有权限的家人仍可共同维护生卒年月、生平和追思资料。",
|
||
guideTitle: "纪念资料范围",
|
||
guides: ["生卒年月与世系关系继续保留", "生平内容由有权限家人维护", "敏感资料继续遵守原有隐私设置"],
|
||
action: "返回成员档案",
|
||
},
|
||
forbidden: {
|
||
eyebrow: "访问受限",
|
||
title: "当前账号没有查看该档案的权限",
|
||
copy: "页面不会展示被隐藏字段,也不会提供绕过家谱权限的入口。",
|
||
guideTitle: "如何申请查看",
|
||
guides: ["先确认已经加入对应家谱", "联系谱主说明亲属关系和用途", "权限变更后重新进入成员档案"],
|
||
action: "返回我的家谱",
|
||
},
|
||
error: {
|
||
eyebrow: "成员状态不可用",
|
||
title: "没有找到要查看的成员",
|
||
copy: "请从成员档案或世系树重新选择成员。",
|
||
guideTitle: "重新进入方式",
|
||
guides: ["从世系树选择成员节点", "从成员目录打开成员档案", "确认当前家谱仍然有效"],
|
||
action: "返回上一页",
|
||
},
|
||
};
|
||
const activeStatus = computed(() => states[statusState.value] || states.error);
|
||
const pageTitle = computed(() => statusState.value === "deceased" ? "成员纪念" : statusState.value === "privacy" ? "隐私资料" : "成员状态");
|
||
const hasValidContext = computed(() => Boolean(genealogyId.value && personId.value));
|
||
const memberIdentityCopy = computed(() => {
|
||
if (!member.value) return "";
|
||
const identity = `${member.value.name} · 第 ${member.value.generation} 世`;
|
||
return ["privacy", "forbidden"].includes(member.value.status)
|
||
? `${identity} · ${member.value.relation}`
|
||
: `${identity} · ${member.value.branch}`;
|
||
});
|
||
|
||
onLoad((query) => {
|
||
genealogyId.value = String(query.genealogyId || "");
|
||
personId.value = String(query.personId || "");
|
||
member.value = hasValidContext.value
|
||
? findTreeMemberPresentationFixture(genealogyId.value, personId.value)
|
||
: null;
|
||
const requestedState = ["privacy", "deceased", "forbidden"].includes(query.state) ? query.state : "";
|
||
const derivedState = member.value?.status || "";
|
||
statusState.value = member.value && (!requestedState || requestedState === derivedState)
|
||
? requestedState || derivedState
|
||
: "error";
|
||
});
|
||
|
||
const handleAction = () => {
|
||
if (statusState.value === "forbidden") {
|
||
return goRoot("G01");
|
||
}
|
||
return goBack();
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||
|
||
.member-status-page { display: flex; min-height: 100vh; flex-direction: column; padding-bottom: 34rpx; box-sizing: border-box; background: $paper; }
|
||
.member-status-page__header, .member-status-context, .status-card, .status-guidance, .status-action { z-index: 2; }
|
||
.member-status-context { @include adaptive.adaptive-tree-field; width: calc(100% - 32rpx); margin: 18rpx auto 0; padding: 16rpx 24rpx; }
|
||
.member-status-context text { display: block; color: $ink-muted; font-size: 23rpx; line-height: 1.45; }
|
||
.member-status-context text:first-child { color: $ink; font-size: 26rpx; font-weight: 700; }
|
||
.status-card { @include adaptive.adaptive-tree-panel; width: calc(100% - 32rpx); min-height: 330rpx; margin: 18rpx auto 0; padding: 12% 10%; }
|
||
.status-card__copy text { display: block; color: $ink-muted; font-size: 24rpx; line-height: 1.55; text-align: center; }
|
||
.status-card__copy text:first-child { color: $brand-red; font-size: 22rpx; letter-spacing: 3rpx; }
|
||
.status-card__copy text:nth-child(2) { margin-top: 12rpx; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 32rpx; font-weight: 700; }
|
||
.status-card__copy text:last-child { margin-top: 14rpx; font-size: 24rpx; }
|
||
.status-guidance { @include adaptive.adaptive-genealogy-list-card; width: calc(100% - 48rpx); margin: 16rpx auto 0; padding: 24rpx 28rpx; }
|
||
.status-guidance text { display: block; color: $ink-muted; font-size: 24rpx; line-height: 1.6; }
|
||
.status-guidance text:first-child { margin-bottom: 8rpx; color: $ink; font-size: 26rpx; font-weight: 700; }
|
||
.status-action { display: grid; width: calc(100% - 72rpx); min-height: 76rpx; margin: 22rpx auto 0; }
|
||
.status-action image, .status-action text { grid-area: 1 / 1; width: 100%; height: 100%; }
|
||
.status-action text { z-index: 1; display: flex; align-items: center; justify-content: center; color: #fff9ed; font-size: 24rpx; font-weight: 700; }
|
||
@media (min-width: 400px) { .member-status-context, .status-card { width: calc(100% - 48rpx); } }
|
||
</style>
|