347 lines
8.8 KiB
Vue
347 lines
8.8 KiB
Vue
<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"
|
|
/>
|
|
<AppButton
|
|
type="secondary"
|
|
block
|
|
label="人生大事"
|
|
@click="toLifeEvents"
|
|
/>
|
|
</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 {
|
|
createRequestController,
|
|
isRequestCancelled
|
|
} from "@/services/api/request-controller.js";
|
|
import { lineageApi } from "@/services/api/lineage-service.js";
|
|
import {
|
|
goBack,
|
|
handleBackPress,
|
|
openPage,
|
|
returnTo,
|
|
} from "@/utils/navigation/gateway.js";
|
|
|
|
const genealogyId = ref("");
|
|
const personId = ref("");
|
|
const personState = ref("loading");
|
|
const personDetailRequestController = createRequestController();
|
|
let loadSequence = 0;
|
|
const person = reactive({
|
|
id: "",
|
|
name: "",
|
|
relation: "",
|
|
generationName: "",
|
|
generation: "",
|
|
aliasName: "",
|
|
sex: "",
|
|
sexLabel: "",
|
|
personStatus: "",
|
|
personStatusLabel: "",
|
|
birthDate: "",
|
|
birthLunar: "",
|
|
birthLunarLabel: "",
|
|
birthplace: "",
|
|
deathDate: "",
|
|
deathLunar: "",
|
|
deathLunarLabel: "",
|
|
deathPlace: "",
|
|
burialPlace: "",
|
|
spouseNames: "",
|
|
biography: "",
|
|
remark: "",
|
|
});
|
|
const detailSections = computed(() => [
|
|
{ title: "别名", copy: person.aliasName },
|
|
{ title: "字辈", copy: person.generationName },
|
|
{ title: "性别", copy: person.sexLabel },
|
|
{ title: "人物状态", copy: person.personStatusLabel },
|
|
{ title: "出生日期", copy: person.birthDate },
|
|
{ title: "出生农历", copy: person.birthLunarLabel },
|
|
{ title: "出生地", copy: person.birthplace },
|
|
{ title: "逝世日期", copy: person.deathDate },
|
|
{ title: "逝世农历", copy: person.deathLunarLabel },
|
|
{ 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 toLifeEvents = () =>
|
|
person.id
|
|
? openPage(
|
|
"R09",
|
|
{ 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 personDetail = await lineageApi.getPerson(genealogyId.value, personId.value, {
|
|
requestController: personDetailRequestController,
|
|
});
|
|
if (activeLoad !== loadSequence) return;
|
|
Object.assign(person, personDetail);
|
|
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
|
|
) {
|
|
personState.value = "error";
|
|
return;
|
|
}
|
|
void loadPerson();
|
|
});
|
|
|
|
onBackPress((event) => handleBackPress(event, requestBack));
|
|
onUnload(() => {
|
|
loadSequence += 1;
|
|
personDetailRequestController.abort();
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import "../../styles/adaptive-frame-profiles.scss";
|
|
|
|
.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-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: clamp(20px, 38rpx, 26px);
|
|
font-weight: 700;
|
|
}
|
|
.person-identity-card__meta {
|
|
margin-top: 8rpx;
|
|
color: $ink-muted;
|
|
font-size: clamp(15px, 25rpx, 18px);
|
|
font-weight: 600;
|
|
}
|
|
.person-identity-card__hint {
|
|
margin-top: 8rpx;
|
|
color: #806a51;
|
|
font-size: clamp(13px, 21rpx, 16px);
|
|
}
|
|
.person-archive-card {
|
|
min-height: 154rpx;
|
|
margin-top: 14rpx;
|
|
padding: 32rpx 42rpx;
|
|
box-sizing: border-box;
|
|
}
|
|
.person-archive-card,
|
|
.person-state-card {
|
|
@include adaptive-records-content;
|
|
}
|
|
.person-archive-card text {
|
|
display: block;
|
|
}
|
|
.person-archive-card text:first-child {
|
|
color: $brand-red;
|
|
font-size: clamp(15px, 24rpx, 18px);
|
|
font-weight: 700;
|
|
}
|
|
.person-archive-card text:last-child {
|
|
margin-top: 11rpx;
|
|
color: $ink;
|
|
font-size: clamp(15px, 24rpx, 18px);
|
|
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-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: clamp(19px, 34rpx, 24px);
|
|
font-weight: 700;
|
|
}
|
|
.person-state-card text:last-child {
|
|
margin-top: 16rpx;
|
|
color: $ink-muted;
|
|
font-size: clamp(15px, 24rpx, 18px);
|
|
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: clamp(19px, 35rpx, 24px);
|
|
}
|
|
}
|
|
</style>
|