5.9 KiB
5.9 KiB
始祖显示和删除后重新定位修复
问题描述
问题1:添加父亲后显示错误
给始祖(第1代成员)添加父亲后:
- 新父亲显示为"孤立成员"(橙色边框)
- 原始祖应该不再按始祖模式显示(金色边框)
- 新父亲应该作为始祖显示
问题2:删除父亲后始祖定位错误
删除始祖后:
- 应该自动找到下一代中年龄最大的直系成员作为新始祖
- 显示模式也要相应调整
根本原因
原因1:使用静态 isFounder 字段
前端代码使用 member.isFounder 字段来判断始祖,但这个字段:
- 在数据库中是静态的,创建时设置后不会自动更新
- 当添加父母后,原始祖的
isFounder仍然是true - 新父母的
isFounder是false或undefined - 导致显示状态不正确
正确的做法是使用 treeData.rootId(来自后端的 rootMemberId):
- 后端在添加/删除成员时会更新
FamilyTree.rootMemberId - API 返回
rootId字段,存储在treeData.rootId中 - 前端应该通过比较
member.id === treeData.rootId来判断始祖
原因2:findNewRootMember 按创建时间排序
findNewRootMember 函数使用 createdAt 排序:
orderBy: {
createdAt: 'asc',
}
这会选择最早创建的成员,而不是年龄最大的成员。
解决方案
1. 修改前端判断逻辑
app/members/page.tsx
-
MemberCard 组件:使用
member.id === currentTreeId判断始祖- 将
currentTreeId改为传递treeData.rootId(而不是currentTree?.rootMemberId) - 修改
isFounder判断逻辑
- 将
-
membersByGeneration 分组逻辑:使用
m.id === treeData.rootId判断始祖- 修改
familyMembers过滤逻辑 - 修改排序逻辑
- 修改
重要:必须使用 treeData.rootId 而不是 currentTree?.rootMemberId,因为:
treeData.rootId是从 API/api/trees/[treeId]/members返回的rootId- 这个值在后端添加/删除成员时会实时更新
currentTree.rootMemberId可能不会及时更新
components/tree/family-node.tsx
- 使用
isRootprop 而不是member.isFounder字段 - 修改两处使用
isFounder的地方
components/tree/d3-org-chart-flow.tsx
- 使用
member.id === rootId判断始祖 - 修改构建节点数据时的
isFounder赋值
2. 修改 findNewRootMember 函数
lib/generation-utils.ts
修改排序逻辑,按出生日期排序(年龄最大的优先):
export async function findNewRootMember(treeId: string): Promise<string | null> {
const firstGenMembers = await prisma.familyMember.findMany({
where: {
treeId,
generation: 1,
fatherId: null,
motherId: null,
},
select: {
id: true,
birthDate: true,
createdAt: true,
},
})
if (firstGenMembers.length === 0) return null
// 按出生日期排序(年龄最大的优先),如果没有出生日期则按创建时间排序
const sorted = firstGenMembers.sort((a, b) => {
// 如果都有出生日期,按出生日期排序(早的在前)
if (a.birthDate && b.birthDate) {
return a.birthDate.localeCompare(b.birthDate)
}
// 如果只有一个有出生日期,有出生日期的优先
if (a.birthDate) return -1
if (b.birthDate) return 1
// 如果都没有出生日期,按创建时间排序
return a.createdAt.getTime() - b.createdAt.getTime()
})
return sorted[0].id
}
修改的文件
-
app/members/page.tsx
- 修改 MemberCard 组件的
isFounder判断 - 修改
currentTreeId传递为rootMemberId - 修改
membersByGeneration分组逻辑
- 修改 MemberCard 组件的
-
components/tree/family-node.tsx
- 使用
isRootprop 替代member.isFounder
- 使用
-
components/tree/d3-org-chart-flow.tsx
- 使用
member.id === rootId判断始祖
- 使用
-
lib/generation-utils.ts
- 修改
findNewRootMember函数,按出生日期排序
- 修改
测试建议
测试场景1:添加父母后的显示
- 创建一个家族树,添加第1代成员(始祖A)
- 给始祖A添加子女(第2代)
- 给始祖A添加父亲(新成员B)
- 验证:
- 新父亲B显示为始祖(金色边框)
- 原始祖A不再显示为始祖(普通蓝色/粉色边框)
- 新父亲B不显示为"孤立成员"
测试场景2:删除始祖后的重新定位
- 创建家族树,有多个第1代成员(兄弟姐妹)
- 设置不同的出生日期
- 在成员详情页删除当前始祖
- 验证:
- 系统自动选择年龄最大的(出生日期最早的)成员作为新始祖
- 页面自动跳转到新始祖的详情页(而不是成员列表页)
- 新始祖显示为金色边框
- 家族树的 rootMemberId 正确更新
- highlightedMemberId 更新为新始祖的ID
测试场景3:在树视图中删除始祖
- 在树视图中删除始祖
- 验证:
- 树视图自动高亮显示新始祖
- 不会尝试定位到已删除的成员
测试场景4:没有出生日期的情况
- 创建多个第1代成员,都没有出生日期
- 删除当前始祖
- 验证:
- 系统按创建时间选择最早创建的成员作为新始祖
技术细节
为什么使用 rootMemberId 而不是 isFounder
- 动态性:
rootMemberId在后端逻辑中会自动更新,而isFounder是静态字段 - 单一真相源:
FamilyTree.rootMemberId是始祖的唯一标识 - 一致性:后端已经在添加/删除成员时维护
rootMemberId,前端应该使用它
为什么按出生日期排序
- 符合传统:中国传统家谱中,年长者为尊
- 逻辑合理:删除始祖后,应该由年龄最大的兄弟姐妹继承始祖位置
- 用户期望:用户期望看到年龄最大的成员作为新始祖
相关功能
- 代数重新计算(GENERATION_RECALCULATION_FIX.md)
- 始祖添加父母警告对话框(GenerationWarningDialog)
- 成员删除逻辑(app/api/trees/[treeId]/members/[memberId]/route.ts)