Files
jiapuapp/pages/tree/t08-member-states.vue
T
2026-07-24 07:56:33 +08:00

155 lines
6.5 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.
<!-- 页面编号T-08用途展示指定成员的隐私纪念或无权限状态 -->
<template>
<view
class="member-status-page"
:class="{
'member-status--available': statusState === 'available',
'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">
<AppLoading
v-if="statusState === 'loading'"
text="正在读取成员状态"
description="请稍候,正在读取人物详情中的状态字段。"
/>
<view v-else class="status-card__copy">
<text>{{ activeStatus.eyebrow }}</text>
<text>{{ activeStatus.title }}</text>
<text>{{ activeStatus.copy }}</text>
</view>
</view>
<view v-if="statusState !== 'loading'" class="status-guidance">
<view>
<text>{{ activeStatus.guideTitle }}</text>
<text v-for="line in activeStatus.guides" :key="line">{{ line }}</text>
</view>
</view>
<view v-if="statusState !== 'loading'" 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, onUnload } from "@dcloudio/uni-app";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
import { goBack } from "@/utils/navigation.js";
const genealogyId = ref("");
const personId = ref("");
const statusState = ref("loading");
const genealogyName = ref("汤氏家谱");
const member = ref(null);
const statusRequestController = createRequestController();
let loadSequence = 0;
const states = {
loading: {
eyebrow: "正在读取",
title: "正在读取成员状态",
copy: "",
guideTitle: "",
guides: [],
action: "",
},
available: {
eyebrow: "人物状态",
title: "当前状态已由服务端返回",
copy: "页面只展示人物详情中的原始状态值,不把未声明的字典值解释成隐私、纪念或访问限制。",
guideTitle: "当前合同范围",
guides: ["人物状态值来自 personStatus 字段", "状态展示文案需要后端提供字典 owner", "当前页不执行停用或任何状态写入"],
action: "返回成员档案",
},
error: {
eyebrow: "成员状态不可用",
title: "没有找到要查看的成员",
copy: "请从成员档案或世系树重新选择成员。",
guideTitle: "重新进入方式",
guides: ["从世系树选择成员节点", "从成员目录打开成员档案", "确认当前家谱仍然有效"],
action: "返回上一页",
},
};
const activeStatus = computed(() => states[statusState.value] || states.error);
const pageTitle = computed(() => "成员状态");
const hasValidContext = computed(() => Boolean(genealogyId.value && personId.value));
const memberIdentityCopy = computed(() => {
if (!member.value) return "";
const status = member.value.personStatus || "未填写";
return `${member.value.name} · 第 ${member.value.generation} 世 · 状态值 ${status}`;
});
const loadMember = async () => {
const activeLoad = ++loadSequence;
statusState.value = "loading";
try {
const result = await appApi.getPerson(genealogyId.value, personId.value, {
requestController: statusRequestController,
});
if (activeLoad !== loadSequence) return;
member.value = result;
genealogyName.value = result.genealogyName;
statusState.value = "available";
} catch (error) {
if (activeLoad !== loadSequence || isRequestCancelled(error)) return;
member.value = null;
statusState.value = "error";
}
};
onLoad((query) => {
genealogyId.value = String(query.genealogyId || "");
personId.value = String(query.personId || "");
if (!hasValidContext.value || query.state === "error") {
statusState.value = "error";
return;
}
void loadMember();
});
onUnload(() => {
loadSequence += 1;
statusRequestController.abort();
});
const handleAction = () => 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>