Files
jiapuapp/pages/tree/index.vue
T

63 lines
4.3 KiB
Vue

<template>
<view class="tree-page">
<PageHeader title="世系树" action="树形视图" />
<view class="tree-toolbar"><view class="branch-select">当前家谱世系</view><view class="tree-tools"><text @click="showSoon">查找</text><text @click="showSoon">编辑</text></view></view>
<scroll-view class="tree-scroll" scroll-x scroll-y>
<view class="tree-canvas">
<view v-if="!members.length" class="tree-empty">{{ loadError || '暂未录入族人' }}</view>
<view v-for="member in members" :key="member.id" class="member-node" :class="{ selected: selected && selected.id === member.id }" :style="nodeStyle(member)" @click="selected = member"><text class="node-name">{{ member.name }}</text><text class="node-relation">{{ member.relation }}</text><text class="node-years">{{ member.years }}</text></view>
</view>
</scroll-view>
<view v-if="selected" class="member-sheet"><view><text class="sheet-name">{{ selected.name }}</text><text class="sheet-meta">第 {{ selected.generation }} 世 · {{ selected.relation }} · {{ selected.branch }}</text></view><view class="sheet-actions"><text @click="toMember">查看资料</text><text @click="showSoon">添加亲属</text></view></view>
</view>
</template>
<script setup>
import { 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 members = ref([])
const selected = ref(null)
const genealogyId = ref('')
const loadError = ref('')
const loadTree = async (query) => {
genealogyId.value = query.genealogyId || genealogyContext.getCurrentGenealogyId()
if (!genealogyId.value) { loadError.value = '未找到当前家谱。'; return }
genealogyContext.setCurrentGenealogyId(genealogyId.value)
try {
members.value = await appApi.getTree(genealogyId.value)
selected.value = members.value.find((item) => String(item.id) === String(query.selectedId)) || members.value[0] || null
} catch (error) {
loadError.value = error.message || '世系树加载失败。'
}
}
onLoad(loadTree)
const nodeStyle = (member) => ({ left: `${member.x}%`, top: `${member.y}%` })
const toMember = () => uni.navigateTo({ url: `/pages/member/detail?genealogyId=${genealogyId.value}&personId=${selected.value.id}` })
const showSoon = () => uni.showToast({ title: '亲属关系编辑将在下一阶段接入', icon: 'none' })
</script>
<style scoped lang="scss">
.tree-page { height: 100vh; overflow: hidden; background: $paper; }
.tree-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 28rpx; border-bottom: 1rpx solid $gold-soft; background: #fffaf0; }
.branch-select { padding: 12rpx 18rpx; border: 1rpx solid $gold; color: $ink; font-size: 25rpx; }
.tree-tools { display: flex; gap: 24rpx; color: $brand-red; font-size: 26rpx; }
.tree-scroll { height: calc(100vh - 382rpx); white-space: nowrap; }
.tree-canvas { position: relative; width: 1180rpx; height: 1120rpx; background: $paper; }
.tree-empty { padding: 240rpx 0; color: $ink-muted; font-size: 28rpx; text-align: center; }
.member-node { position: absolute; width: 150rpx; min-height: 148rpx; padding: 20rpx 12rpx; box-sizing: border-box; border: 2rpx solid $gold-soft; background: #fffaf0; transform: translateX(-50%); white-space: normal; text-align: center; }
.member-node.selected { border-color: $brand-red; box-shadow: 0 0 0 6rpx #f2dfcf; }
.node-name { display: block; color: $ink; font-family: serif; font-size: 32rpx; font-weight: 700; }
.node-relation { display: block; margin-top: 10rpx; color: $brand-red; font-size: 22rpx; }
.node-years { display: block; margin-top: 8rpx; color: $ink-muted; font-size: 20rpx; }
.member-sheet { position: fixed; right: 0; bottom: 0; left: 0; display: flex; align-items: center; justify-content: space-between; padding: 26rpx 28rpx calc(26rpx + env(safe-area-inset-bottom)); border-top: 1rpx solid $gold; background: $navy; color: #fff8ec; }
.sheet-name { display: block; font-family: serif; font-size: 36rpx; font-weight: 700; }
.sheet-meta { display: block; margin-top: 8rpx; color: #dfc590; font-size: 21rpx; }
.sheet-actions { display: flex; flex-direction: column; gap: 12rpx; color: #f3dba7; font-size: 24rpx; text-align: right; }
</style>