Files
jiapuapp/pages/genealogy/g05-genealogy-overview.vue
T
2026-07-15 08:31:33 +08:00

160 lines
9.6 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.
<!-- 页面编号G-05用途家谱总览与加载空数据失败状态 -->
<template>
<view class="overview-page">
<image class="overview-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="overview-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<view class="overview-page__header">
<PageHeader title="家谱总览" :action="overviewState === 'ready' ? '管理' : ''" @action="toSettings" />
</view>
<view class="overview-surface">
<image class="overview-surface__skin" src="/static/assets/modules/genealogy/opaque/g05-overview-surface.png" mode="scaleToFill" />
<template v-if="overviewState === 'ready' && genealogy">
<view class="overview-ready">
<view class="overview-hero">
<text class="overview-hero__kicker">{{ genealogy.hall || '堂号待补' }} · {{ genealogy.location || '所在地待补' }}</text>
<text class="overview-hero__title">{{ genealogy.name }}</text>
<text class="overview-hero__motto">{{ genealogy.motto || '敦亲睦族,敬祖传家。' }}</text>
<view class="overview-hero__stats">
<text> {{ genealogy.memberCount || 0 }} </text>
<text>已激活 {{ genealogy.activeCount || 0 }} </text>
<text>{{ genealogy.visibility || '仅成员可见' }}</text>
</view>
</view>
<view class="overview-actions">
<view class="overview-action overview-action--tree" @click="toTree">
<text class="overview-action__title">世系树</text><text>查看家脉关系</text>
</view>
<view class="overview-action overview-action--ancestor" @click="toFirstPerson">
<text class="overview-action__title">录入族人</text><text>从首代开始完善</text>
</view>
<view class="overview-action overview-action--poem" @click="toGenerationPoems">
<text class="overview-action__title">字辈谱</text><text>传承行辈次序</text>
</view>
<view class="overview-action overview-action--review" @click="toApplications">
<text class="overview-action__title">入谱审核</text><text>处理加入申请</text>
</view>
</view>
<view class="overview-family" @click="toFamily">
<text>家族近况</text><text>进入家族圈查看动态</text><text>查看</text>
</view>
<view class="overview-note">
<text class="overview-note__title">家谱资料仅向家人开放</text>
<text>公开范围访问说明与管理权由家谱管理员在设置中维护</text>
</view>
</view>
</template>
<view v-else class="overview-state-panel" :class="{
'overview-state--loading': overviewState === 'loading',
'overview-state--empty': overviewState === 'empty',
'overview-state--error': overviewState === 'error'
}">
<text class="overview-state-panel__title">{{ stateTitle }}</text>
<text class="overview-state-panel__copy">{{ stateCopy }}</text>
<view v-if="overviewState !== 'loading'" class="overview-state-panel__action" @click="overviewState === 'error' ? reloadOverview() : toGenealogies()">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<text>{{ overviewState === 'error' ? '重新查看' : '返回我的家谱' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import { appApi } from '@/utils/api.js'
import { genealogyContext } from '@/utils/genealogy-context.js'
const genealogy = ref(null)
const genealogyId = ref('')
const overviewState = ref('loading')
const loadError = ref('')
const stateTitle = computed(() => ({
loading: '正在展开家谱…',
empty: '还没有可查看的家谱',
error: '家谱暂时无法打开'
}[overviewState.value] || ''))
const stateCopy = computed(() => loadError.value || ({
loading: '请稍候,正在读取家谱概览。',
empty: '请从“我的家谱”选择一部家谱后再进入。',
error: '可能是网络波动、家谱不存在或当前账号无访问权限。'
}[overviewState.value] || ''))
const loadGenealogy = async (query = {}) => {
overviewState.value = 'loading'
loadError.value = ''
genealogy.value = null
genealogyId.value = query.genealogyId || genealogyId.value || genealogyContext.getCurrentGenealogyId()
if (query.state === 'empty' || !genealogyId.value) {
overviewState.value = 'empty'
return
}
if (query.state === 'error') {
overviewState.value = 'error'
return
}
genealogyContext.setCurrentGenealogyId(genealogyId.value)
try {
genealogy.value = await appApi.getOverview(genealogyId.value)
overviewState.value = genealogy.value ? 'ready' : 'error'
if (!genealogy.value) loadError.value = '家谱不存在或当前账号无权访问。'
} catch (error) {
loadError.value = error.message || '家谱加载失败,请稍后重试。'
overviewState.value = 'error'
}
}
onLoad(loadGenealogy)
const reloadOverview = () => loadGenealogy({ genealogyId: genealogyId.value })
const toGenealogies = () => uni.reLaunch({ url: '/pages/genealogy/g01-my-genealogies' })
const toTree = () => uni.navigateTo({ url: `/pages/tree/t01-tree-overview?genealogyId=${genealogyId.value}` })
const toFirstPerson = () => uni.navigateTo({ url: `/pages/genealogy/g03-create-genealogy?step=ancestor&genealogyId=${genealogyId.value}` })
const toFamily = () => uni.reLaunch({ url: '/pages/family/f01-family-feed' })
const toApplications = () => uni.navigateTo({ url: `/pages/genealogy/g10-application-review?genealogyId=${genealogyId.value}` })
const toSettings = () => uni.navigateTo({ url: `/pages/genealogy/g11-genealogy-settings?genealogyId=${genealogyId.value}` })
const toGenerationPoems = () => uni.navigateTo({ url: `/pages/genealogy/g12-generation-poems?genealogyId=${genealogyId.value}` })
</script>
<style scoped lang="scss">
.overview-page { position: relative; min-height: 100vh; overflow: hidden; background: $paper; }
.overview-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.overview-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.overview-page__header { position: relative; z-index: 3; }
.overview-surface { position: relative; z-index: 2; width: 100%; height: min(483px, calc(100vw * 1.3422)); }
.overview-surface__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.overview-ready { position: absolute; inset: 0; }
.overview-hero { position: absolute; top: 6.5%; right: 8%; left: 8%; color: #fff8ec; }
.overview-hero__kicker { display: block; color: #dec483; font-size: 22rpx; letter-spacing: 3rpx; }
.overview-hero__title { display: block; margin-top: 12rpx; font-family: 'STKaiti', 'KaiTi', serif; font-size: 46rpx; font-weight: 700; letter-spacing: 4rpx; }
.overview-hero__motto { display: block; margin-top: 6rpx; color: #e9dcc1; font-size: 23rpx; }
.overview-hero__stats { display: flex; gap: 18rpx; margin-top: 12rpx; color: #dbc990; font-size: 22rpx; }
.overview-actions { position: absolute; top: 29.2%; right: 7.2%; left: 7.2%; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 3.4% 3%; height: 36.2%; }
.overview-action { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: 0 22rpx; box-sizing: border-box; color: $ink-muted; font-size: 22rpx; }
.overview-action__title { margin-bottom: 9rpx; color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 30rpx; font-weight: 700; }
.overview-action--tree .overview-action__title { color: $brand-red; }
.overview-family { position: absolute; top: 70.2%; right: 7.5%; left: 7.5%; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16rpx; color: $ink-muted; font-size: 22rpx; }
.overview-family text:first-child { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 28rpx; font-weight: 700; }
.overview-family text:last-child { color: $brand-red; }
.overview-note { position: absolute; top: 80.5%; right: 8.5%; left: 8.5%; display: flex; flex-direction: column; justify-content: center; height: 15%; color: $ink-muted; font-size: 22rpx; line-height: 1.6; }
.overview-note__title { margin-bottom: 8rpx; color: $brand-red; font-family: 'STKaiti', 'KaiTi', serif; font-size: 28rpx; font-weight: 700; }
.overview-state-panel { position: absolute; top: 29%; right: 8%; left: 8%; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 48%; padding: 0 40rpx; box-sizing: border-box; text-align: center; }
.overview-state-panel__title { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 34rpx; font-weight: 700; }
.overview-state-panel__copy { max-width: 480rpx; margin-top: 18rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.7; }
.overview-state-panel__action { position: relative; width: 300rpx; height: 76rpx; margin-top: 30rpx; }
.overview-state-panel__action image { position: absolute; inset: 0; width: 100%; height: 100%; }
.overview-state-panel__action text { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; height: 100%; color: #fff9ed; font-size: 25rpx; font-weight: 700; }
@media (min-width: 400px) {
.overview-hero { top: 7%; }
.overview-action { padding: 0 28rpx; }
}
</style>