Files
jiapuapp/pages/records/r02-person-detail.vue
T
2026-07-27 06:50:23 +08:00

194 lines
8.3 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.
<!-- 页面编号R-02用途人物录详情与不写库的人物资料预览 -->
<template>
<view class="person-detail-page" :class="`person-detail-state--${personState}`">
<ModulePageBackground module="records" />
<view class="person-detail-header">
<PageHeader
title="人物详情"
custom-back
@back="requestBack"
/>
</view>
<view v-if="personState === 'loading'" class="person-detail-loading">
<AppLoading text="正在读取人物档案" description="请稍候,正在整理人物资料。" />
</view>
<view v-else class="person-detail-content">
<template v-if="personState === 'detail'">
<view class="person-identity-card">
<view class="person-identity-card__copy">
<text class="person-identity-card__name">{{ person.name || "待填写姓名" }}</text>
<text class="person-identity-card__meta">{{ person.relation || "人物预览" }} · {{ person.generation || "—" }} </text>
<text class="person-identity-card__hint">人物录档案</text>
</view>
</view>
</template>
<template v-if="personState === 'detail'">
<view v-for="item in detailSections" :key="item.title" class="person-archive-card">
<view><text>{{ item.title }}</text><text>{{ item.copy || "未填写" }}</text></view>
</view>
<view class="person-related-actions">
<AppButton type="secondary" block label="成长日志" @click="toGrowthJournal" />
<view class="person-related-unavailable"><text>人生大事</text><text>暂未开放</text></view>
</view>
<view class="person-edit-action" @click="toMemberProfile"><AppButton block label="查看成员档案" /></view>
</template>
<view v-else class="person-state-card">
<view>
<text>{{ personState === 'expired' ? '人物档案已失效' : '人物档案暂不可用' }}</text>
<text>{{ personState === 'expired' ? '这份人物资料不存在或不属于当前家谱。' : '请返回人物录重新选择,页面不会回退到其他人物。' }}</text>
</view>
<view class="person-state-action"><AppButton type="secondary" block label="返回人物录" @click="returnToPeople" /></view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from "vue";
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
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,
handleBackPress,
openPage,
returnTo,
} from "@/utils/navigation.js";
const genealogyId = ref("");
const personId = ref("");
const personState = ref("loading");
const personRequestController = createRequestController();
let loadSequence = 0;
const person = reactive({
id: "",
name: "",
relation: "",
generationName: "",
generation: "",
aliasName: "",
sex: "",
personStatus: "",
birthDate: "",
birthLunar: "",
birthplace: "",
deathDate: "",
deathLunar: "",
deathPlace: "",
burialPlace: "",
spouseNames: "",
biography: "",
remark: "",
});
const detailSections = computed(() => [
{ title: "别名", copy: person.aliasName },
{ title: "字辈", copy: person.generationName },
{ title: "性别(字典值)", copy: person.sex },
{ title: "人物状态(字典值)", copy: person.personStatus },
{ title: "出生日期", copy: person.birthDate },
{ title: "出生农历", copy: person.birthLunar },
{ title: "出生地", copy: person.birthplace },
{ title: "逝世日期", copy: person.deathDate },
{ title: "逝世农历", copy: person.deathLunar },
{ title: "逝世地", copy: person.deathPlace },
{ title: "安葬地", copy: person.burialPlace },
{ title: "配偶", copy: person.spouseNames },
{ title: "人物小传", copy: person.biography },
{ title: "档案备注", copy: person.remark },
]);
const requestBack = () => goBack();
const returnToPeople = () =>
genealogyId.value
? returnTo("R01", { genealogyId: genealogyId.value })
: goBack();
const toGrowthJournal = () =>
person.id
? openPage(
"R08",
{ genealogyId: genealogyId.value, personId: personId.value },
"R02",
)
: Promise.resolve(false);
const toMemberProfile = () =>
person.id
? openPage(
"T03",
{ genealogyId: genealogyId.value, personId: person.id },
"R02",
)
: Promise.resolve(false);
const loadPerson = async () => {
const activeLoad = ++loadSequence;
personState.value = "loading";
try {
const result = await appApi.getPerson(genealogyId.value, personId.value, {
requestController: personRequestController,
});
if (activeLoad !== loadSequence) return;
Object.assign(person, result);
personState.value = "detail";
} catch (error) {
if (activeLoad !== loadSequence || isRequestCancelled(error)) return;
personState.value = "expired";
}
};
onLoad((query) => {
genealogyId.value = String(query.genealogyId || "");
personId.value = String(query.personId || "");
if (query.mode !== "view" || !genealogyId.value || !personId.value || query.state === "error") {
personState.value = "error";
return;
}
void loadPerson();
});
onBackPress((event) => handleBackPress(event, requestBack));
onUnload(() => {
loadSequence += 1;
personRequestController.abort();
});
</script>
<style scoped lang="scss">
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
.person-detail-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.person-detail-header,.person-detail-loading,.person-detail-content { z-index: 1; }
.person-detail-loading { min-height: calc(100vh - 100rpx); }
.person-detail-content { padding: 18rpx 24rpx 72rpx; }
.person-identity-card { @include adaptive.adaptive-records-person; display: flex; min-height: 190rpx; padding: 30rpx 10%; }
.person-identity-card__copy { display: flex; flex: 1; flex-direction: column; justify-content: center; }
.person-identity-card__copy text { display: block; }
.person-identity-card__name { color: $ink; font-family: STKaiti,KaiTi,serif; font-size: 38rpx; font-weight: 700; }
.person-identity-card__meta { margin-top: 8rpx; color: $ink-muted; font-size: 25rpx; font-weight: 600; }
.person-identity-card__hint { margin-top: 8rpx; color: #806a51; font-size: 21rpx; }
.person-archive-card { min-height: 154rpx; margin-top: 14rpx; padding: 32rpx 42rpx; box-sizing: border-box; }
.person-archive-card,.person-state-card { @include adaptive.adaptive-records-content; }
.person-archive-card text { display: block; }
.person-archive-card text:first-child { color: $brand-red; font-size: 24rpx; font-weight: 700; }
.person-archive-card text:last-child { margin-top: 11rpx; color: $ink; font-size: 24rpx; line-height: 1.55; }
.person-edit-action { margin-top: 20rpx; }
.person-related-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14rpx; margin-top: 18rpx; }
.person-related-unavailable { display:flex; min-height:88rpx; flex-direction:column; align-items:center; justify-content:center; box-sizing:border-box; border:1rpx solid rgba(128,89,49,.24); border-radius:8rpx; background:rgba(255,252,245,.38); color:$ink-muted; }
.person-related-unavailable text:first-child { font-size:25rpx; font-weight:700; }
.person-related-unavailable text:last-child { margin-top:4rpx; font-size:20rpx; }
.person-state-card { display: flex; flex-direction: column; min-height: 300rpx; margin-top: 26rpx; padding: 72rpx 54rpx 42rpx; box-sizing: border-box; text-align: center; }
.person-state-card text { display: block; }
.person-state-card text:first-child { color: $ink; font-family: STKaiti,KaiTi,serif; font-size: 34rpx; font-weight: 700; }
.person-state-card text:last-child { margin-top: 16rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.6; }
.person-state-action { margin: 24rpx 48rpx 0; }
@media (min-width: 400px) { .person-detail-content { padding-right: 32rpx; padding-left: 32rpx; } }
@media (max-width: 340px) { .person-detail-content { padding-right: 18rpx; padding-left: 18rpx; } .person-identity-card__name { font-size: 35rpx; } }
</style>