完成50%
This commit is contained in:
@@ -2,18 +2,27 @@
|
||||
<template>
|
||||
<view
|
||||
class="member-page"
|
||||
:data-current-person-id="personId"
|
||||
:data-trail-length="memberTrail.length"
|
||||
:data-trail-index="trailIndex"
|
||||
:class="{
|
||||
'member-state--detail': memberState === 'detail',
|
||||
'member-state--restricted': memberState === 'restricted',
|
||||
'member-state--error': memberState === 'error',
|
||||
}"
|
||||
>
|
||||
<ModulePageBackground module="tree" />
|
||||
<view class="member-page__header">
|
||||
<PageHeader title="成员档案" :action="memberState === 'detail' && canEdit ? '编辑' : ''" @action="toEdit" />
|
||||
<PageHeader title="成员档案"
|
||||
:action="canPreviewEdit ? '编辑预览' : ''"
|
||||
custom-back
|
||||
@back="requestBack"
|
||||
@action="toEdit"
|
||||
/>
|
||||
</view>
|
||||
<view class="member-context">
|
||||
<text>{{ genealogyName }}</text>
|
||||
<text>{{ memberState === "detail" ? "成员身份与亲属关系" : "请重新选择成员" }}</text>
|
||||
<text>{{ memberContextDescription }}</text>
|
||||
</view>
|
||||
|
||||
<view class="member-panel">
|
||||
@@ -28,7 +37,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="member-status-entry" @click="openMemberState(member.status)">
|
||||
<view class="member-status-entry" @click="openMemberState">
|
||||
<text>{{ statusLabel }}</text>
|
||||
<text>{{ statusDescription }}</text>
|
||||
<text>查看</text>
|
||||
@@ -41,13 +50,36 @@
|
||||
|
||||
<text class="member-section-title member-section-title--relation">亲属关系</text>
|
||||
<view class="member-relatives">
|
||||
<view v-for="relative in member.relatives" :key="relative.id" @click="openRelative(relative)">
|
||||
<view
|
||||
v-for="relative in member.relatives"
|
||||
:key="relative.id"
|
||||
:data-person-id="relative.id"
|
||||
@click="openRelative(relative.id)"
|
||||
>
|
||||
<text>{{ relative.relation }}</text><text>{{ relative.name }}</text>
|
||||
</view>
|
||||
<text v-if="!member.relatives.length">尚未记录可查看的亲属</text>
|
||||
</view>
|
||||
|
||||
<view v-if="canEdit" class="member-profile-action" @click="toEdit"><text>完善成员档案</text></view>
|
||||
<view class="member-record-actions">
|
||||
<view role="button" aria-label="查看成长日志" @click="toGrowthJournal"><text>成长日志</text></view>
|
||||
<view role="button" aria-label="查看人生事服务状态" @click="toLifeEvents"><text>人生事(待开放)</text></view>
|
||||
</view>
|
||||
|
||||
<view v-if="canPreviewEdit" class="member-profile-action" @click="toEdit"><text>制作成员编辑预览</text></view>
|
||||
</view>
|
||||
|
||||
<view v-else-if="memberState === 'restricted' && member" class="member-restricted">
|
||||
<view class="member-heading">
|
||||
<view class="member-heading__seal"><text>{{ member.name.slice(0, 1) }}</text></view>
|
||||
<view>
|
||||
<text>{{ member.name }}</text>
|
||||
<text>第 {{ member.generation }} 世 · {{ member.relation }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text>{{ restrictedCopy.title }}</text>
|
||||
<text>{{ restrictedCopy.copy }}</text>
|
||||
<view class="member-restricted-action" @click="toTree"><text>返回上一位成员</text></view>
|
||||
</view>
|
||||
|
||||
<view v-else class="member-error">
|
||||
@@ -60,12 +92,18 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { onBackPress, onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { genealogyContext } from "@/utils/genealogy-context.js";
|
||||
import { findTreeMemberPresentationFixture } from "@/data/mock.js";
|
||||
import {
|
||||
consumeNavigationResult,
|
||||
handleBackPress,
|
||||
openPage,
|
||||
runBackGuard,
|
||||
} from "@/utils/navigation.js";
|
||||
|
||||
const genealogyId = ref("");
|
||||
const personId = ref("");
|
||||
@@ -73,24 +111,9 @@ const memberState = ref("loading");
|
||||
const member = ref(null);
|
||||
const errorMessage = ref("");
|
||||
const genealogyName = ref("汤氏家谱");
|
||||
const memberTrail = reactive([]);
|
||||
const trailIndex = ref(-1);
|
||||
|
||||
const memberFixtures = {
|
||||
101: {
|
||||
id: 101, name: "汤文远", generation: 12, relation: "始祖", branch: "主支", generationName: "文字辈",
|
||||
birthDate: "1940年3月", years: "1940—2012", birthplace: "河南南阳", status: "deceased", canEdit: true,
|
||||
relatives: [{ id: 102, name: "汤正国", relation: "长子" }, { id: 103, name: "汤正华", relation: "次子" }],
|
||||
},
|
||||
102: {
|
||||
id: 102, name: "汤正国", generation: 13, relation: "长子", branch: "长房", generationName: "正字辈",
|
||||
birthDate: "1965年5月", years: "1965—", birthplace: "河南洛阳", status: "privacy", canEdit: true,
|
||||
relatives: [{ id: 101, name: "汤文远", relation: "父亲" }, { id: 104, name: "汤凯", relation: "长子" }],
|
||||
},
|
||||
103: {
|
||||
id: 103, name: "汤正华", generation: 13, relation: "次子", branch: "二房", generationName: "正字辈",
|
||||
birthDate: "", years: "资料受限", birthplace: "", status: "forbidden", canEdit: false,
|
||||
relatives: [{ id: 101, name: "汤文远", relation: "父亲" }],
|
||||
},
|
||||
};
|
||||
const details = computed(() => member.value ? [
|
||||
{ label: "字辈", value: member.value.generationName },
|
||||
{ label: "出生日期", value: member.value.status === "forbidden" ? "按权限隐藏" : member.value.birthDate },
|
||||
@@ -98,39 +121,147 @@ const details = computed(() => member.value ? [
|
||||
{ label: "祖居地", value: member.value.status === "forbidden" ? "按权限隐藏" : member.value.birthplace },
|
||||
{ label: "所属支系", value: member.value.branch },
|
||||
] : []);
|
||||
const canEdit = computed(() => Boolean(member.value?.canEdit));
|
||||
const canPreviewEdit = computed(() => memberState.value === "detail");
|
||||
const restrictedCopy = computed(() =>
|
||||
member.value?.status === "forbidden"
|
||||
? {
|
||||
title: "当前成员档案访问受限",
|
||||
copy: "页面只保留基础身份,不展示生平、居住地、支系、亲属或编辑入口。",
|
||||
}
|
||||
: {
|
||||
title: "部分成员资料未公开",
|
||||
copy: "页面只保留姓名、关系与世代,不展示其他资料或操作入口。",
|
||||
},
|
||||
);
|
||||
const statusLabel = computed(() => ({ privacy: "隐私资料", deceased: "离世纪念", forbidden: "访问受限" })[member.value?.status] || "成员状态");
|
||||
const statusDescription = computed(() => ({
|
||||
privacy: "部分资料仅向授权成员展示",
|
||||
deceased: "查看生平保留与纪念资料说明",
|
||||
forbidden: "当前账号只能查看有限身份信息",
|
||||
})[member.value?.status] || "查看成员状态说明");
|
||||
const memberContextDescription = computed(() => ({
|
||||
loading: "正在读取成员资料",
|
||||
detail: "成员身份与亲属关系",
|
||||
restricted: "隐私成员仅展示基础身份",
|
||||
error: "请重新选择成员",
|
||||
}[memberState.value] || "请重新选择成员"));
|
||||
|
||||
const loadMember = async (nextPersonId) => {
|
||||
const normalizedPersonId = String(nextPersonId || "");
|
||||
const fixture = findTreeMemberPresentationFixture(genealogyId.value, normalizedPersonId);
|
||||
if (!fixture) {
|
||||
errorMessage.value = "这位成员不存在或已不属于当前家谱。";
|
||||
return false;
|
||||
}
|
||||
const restricted = ["privacy", "forbidden"].includes(fixture.status);
|
||||
const relatives = restricted ? [] : fixture.relatives
|
||||
.map((relative) => {
|
||||
const relativeMember = findTreeMemberPresentationFixture(
|
||||
genealogyId.value,
|
||||
relative.personId,
|
||||
);
|
||||
return relativeMember
|
||||
? { id: relativeMember.id, name: relativeMember.name, relation: relative.relation }
|
||||
: null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
personId.value = normalizedPersonId;
|
||||
member.value = {
|
||||
...fixture,
|
||||
relatives,
|
||||
};
|
||||
errorMessage.value = "";
|
||||
memberState.value = restricted ? "restricted" : "detail";
|
||||
return true;
|
||||
};
|
||||
|
||||
const initializeMemberTrail = async (initialPersonId) => {
|
||||
memberTrail.splice(0, memberTrail.length);
|
||||
trailIndex.value = -1;
|
||||
const normalizedPersonId = String(initialPersonId || "");
|
||||
const loaded = await loadMember(normalizedPersonId);
|
||||
if (!loaded) return false;
|
||||
memberTrail.splice(0, memberTrail.length, normalizedPersonId);
|
||||
trailIndex.value = 0;
|
||||
return true;
|
||||
};
|
||||
|
||||
const openRelative = async (nextPersonId) => {
|
||||
const normalizedPersonId = String(nextPersonId || "");
|
||||
if (!normalizedPersonId || normalizedPersonId === personId.value) return false;
|
||||
|
||||
// 先读取目标成员,只有真实读取成功后才截断前进历史并推进轨迹;失败时
|
||||
// 当前成员、当前索引和既有历史全部保持不变。
|
||||
const loaded = await loadMember(normalizedPersonId);
|
||||
if (!loaded) return false;
|
||||
memberTrail.splice(trailIndex.value + 1);
|
||||
memberTrail.push(normalizedPersonId);
|
||||
trailIndex.value = memberTrail.length - 1;
|
||||
return true;
|
||||
};
|
||||
|
||||
const popMemberTrail = async () => {
|
||||
while (trailIndex.value > 0) {
|
||||
const targetIndex = trailIndex.value - 1;
|
||||
const historicalPersonId = memberTrail[targetIndex];
|
||||
const loaded = await loadMember(historicalPersonId);
|
||||
if (loaded) {
|
||||
trailIndex.value = targetIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 历史成员可能已删除或失去权限。移除失效项后,当前成员在数组中的
|
||||
// 索引同步左移,再继续寻找更早的有效成员,绝不创建新的 T03 页面。
|
||||
memberTrail.splice(targetIndex, 1);
|
||||
trailIndex.value -= 1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
internalTrail: trailIndex.value > 0,
|
||||
"pop-internal-trail": popMemberTrail,
|
||||
});
|
||||
|
||||
onLoad((query) => {
|
||||
genealogyId.value = query.genealogyId || genealogyContext.getCurrentGenealogyId() || "";
|
||||
personId.value = query.personId || "";
|
||||
if (genealogyId.value) genealogyContext.setCurrentGenealogyId(genealogyId.value);
|
||||
if (query.state === "loading") { memberState.value = "loading"; return; }
|
||||
if (query.state === "error" || !personId.value || !memberFixtures[personId.value]) {
|
||||
genealogyId.value = String(query.genealogyId || "");
|
||||
const initialPersonId = String(query.personId || "");
|
||||
if (query.state === "loading") return;
|
||||
if (query.state === "error" || !genealogyId.value || !initialPersonId) {
|
||||
memberState.value = "error";
|
||||
errorMessage.value = !personId.value ? "没有指定成员,请从世系树重新选择。" : "这位成员不存在或已不属于当前家谱。";
|
||||
errorMessage.value = !initialPersonId
|
||||
? "没有指定成员,请从世系树重新选择。"
|
||||
: "当前家谱上下文无效,请重新进入。";
|
||||
return;
|
||||
}
|
||||
member.value = { ...memberFixtures[personId.value] };
|
||||
memberState.value = "detail";
|
||||
void initializeMemberTrail(initialPersonId).then((loaded) => {
|
||||
if (loaded) return;
|
||||
member.value = null;
|
||||
memberState.value = "error";
|
||||
});
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
const result = consumeNavigationResult("T03");
|
||||
if (result?.operation === "member-open-requested" && result.entityId) {
|
||||
void openRelative(result.entityId);
|
||||
}
|
||||
});
|
||||
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
|
||||
const toEdit = () => {
|
||||
if (!canEdit.value) { openMemberState("forbidden"); return; }
|
||||
uni.navigateTo({ url: `/pages/tree/t05-edit-member?genealogyId=${genealogyId.value}&personId=${personId.value}` });
|
||||
if (!canPreviewEdit.value) return false;
|
||||
return openPage("T05", { genealogyId: genealogyId.value, personId: personId.value }, "T03");
|
||||
};
|
||||
const openMemberState = (state) =>
|
||||
uni.navigateTo({
|
||||
url: `/pages/tree/t08-member-states?genealogyId=${genealogyId.value}&personId=${personId.value}&state=${state}`,
|
||||
});
|
||||
const openRelative = (relative) =>
|
||||
uni.navigateTo({ url: `/pages/tree/t03-member-profile?genealogyId=${genealogyId.value}&personId=${relative.id}` });
|
||||
const toTree = () => uni.navigateBack();
|
||||
const openMemberState = () =>
|
||||
openPage("T08", { genealogyId: genealogyId.value, personId: personId.value }, "T03");
|
||||
const toGrowthJournal = () =>
|
||||
openPage("R08", { genealogyId: genealogyId.value, personId: personId.value }, "T03");
|
||||
const toLifeEvents = () =>
|
||||
openPage("R09", { genealogyId: genealogyId.value, personId: personId.value }, "T03");
|
||||
const toTree = requestBack;
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -160,8 +291,17 @@ const toTree = () => uni.navigateBack();
|
||||
.member-relatives > view { display: flex; justify-content: space-between; gap: 20rpx; padding: 12rpx 18rpx; color: $ink; font-size: 23rpx; }
|
||||
.member-relatives > view text:last-child { color: $brand-red; }
|
||||
.member-relatives > text { display: block; color: $ink-muted; font-size: 23rpx; line-height: 1.5; }
|
||||
.member-record-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12rpx; margin-top: 20rpx; }
|
||||
.member-record-actions > view { @include adaptive.adaptive-scroll-button(secondary); display: flex; min-height: 72rpx; align-items: center; justify-content: center; padding: 8rpx 14rpx; text-align: center; }
|
||||
.member-record-actions text { color: $brand-red; font-size: 22rpx; font-weight: 700; line-height: 1.35; }
|
||||
.member-profile-action { @include adaptive.adaptive-scroll-button(primary); display: flex; min-height: 76rpx; align-items: center; justify-content: center; margin-top: 22rpx; }
|
||||
.member-profile-action text { color: #fff9ed; font-size: 24rpx; font-weight: 700; }
|
||||
.member-restricted { padding-top: 14rpx; }
|
||||
.member-restricted > text { display: block; color: $ink-muted; font-size: 23rpx; line-height: 1.6; text-align: center; }
|
||||
.member-restricted > text:first-of-type { margin-top: 28rpx; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 31rpx; font-weight: 700; }
|
||||
.member-restricted > text + text { margin-top: 12rpx; }
|
||||
.member-restricted-action { @include adaptive.adaptive-scroll-button(secondary); display: flex; min-height: 76rpx; align-items: center; justify-content: center; margin-top: 22rpx; }
|
||||
.member-restricted-action text { color: $brand-red; font-size: 24rpx; font-weight: 700; }
|
||||
.member-error { margin-top: 30%; text-align: center; }
|
||||
.member-error > text { display: block; color: $ink-muted; font-size: 24rpx; line-height: 1.55; }
|
||||
.member-error > text:nth-child(2) { font-size: 24rpx; }
|
||||
|
||||
Reference in New Issue
Block a user