Files
chinese-family-tree-2/ROOT_MEMBER_DISPLAY_FIX.md
T
freedakgmail 5c8c70097c 0.8.0.0
2025-12-21 17:32:33 +08:00

175 lines
5.9 KiB
Markdown
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.
# 始祖显示和删除后重新定位修复
## 问题描述
### 问题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` 来判断始祖
### 原因2findNewRootMember 按创建时间排序
`findNewRootMember` 函数使用 `createdAt` 排序:
```typescript
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
- 使用 `isRoot` prop 而不是 `member.isFounder` 字段
- 修改两处使用 `isFounder` 的地方
#### components/tree/d3-org-chart-flow.tsx
- 使用 `member.id === rootId` 判断始祖
- 修改构建节点数据时的 `isFounder` 赋值
### 2. 修改 findNewRootMember 函数
#### lib/generation-utils.ts
修改排序逻辑,按出生日期排序(年龄最大的优先):
```typescript
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
}
```
## 修改的文件
1. **app/members/page.tsx**
- 修改 MemberCard 组件的 `isFounder` 判断
- 修改 `currentTreeId` 传递为 `rootMemberId`
- 修改 `membersByGeneration` 分组逻辑
2. **components/tree/family-node.tsx**
- 使用 `isRoot` prop 替代 `member.isFounder`
3. **components/tree/d3-org-chart-flow.tsx**
- 使用 `member.id === rootId` 判断始祖
4. **lib/generation-utils.ts**
- 修改 `findNewRootMember` 函数,按出生日期排序
## 测试建议
### 测试场景1:添加父母后的显示
1. 创建一个家族树,添加第1代成员(始祖A)
2. 给始祖A添加子女(第2代)
3. 给始祖A添加父亲(新成员B
4. 验证:
- 新父亲B显示为始祖(金色边框)
- 原始祖A不再显示为始祖(普通蓝色/粉色边框)
- 新父亲B不显示为"孤立成员"
### 测试场景2:删除始祖后的重新定位
1. 创建家族树,有多个第1代成员(兄弟姐妹)
2. 设置不同的出生日期
3. 在成员详情页删除当前始祖
4. 验证:
- 系统自动选择年龄最大的(出生日期最早的)成员作为新始祖
- 页面自动跳转到新始祖的详情页(而不是成员列表页)
- 新始祖显示为金色边框
- 家族树的 rootMemberId 正确更新
- highlightedMemberId 更新为新始祖的ID
### 测试场景3:在树视图中删除始祖
1. 在树视图中删除始祖
2. 验证:
- 树视图自动高亮显示新始祖
- 不会尝试定位到已删除的成员
### 测试场景4:没有出生日期的情况
1. 创建多个第1代成员,都没有出生日期
2. 删除当前始祖
3. 验证:
- 系统按创建时间选择最早创建的成员作为新始祖
## 技术细节
### 为什么使用 rootMemberId 而不是 isFounder
1. **动态性**`rootMemberId` 在后端逻辑中会自动更新,而 `isFounder` 是静态字段
2. **单一真相源**`FamilyTree.rootMemberId` 是始祖的唯一标识
3. **一致性**:后端已经在添加/删除成员时维护 `rootMemberId`,前端应该使用它
### 为什么按出生日期排序
1. **符合传统**:中国传统家谱中,年长者为尊
2. **逻辑合理**:删除始祖后,应该由年龄最大的兄弟姐妹继承始祖位置
3. **用户期望**:用户期望看到年龄最大的成员作为新始祖
## 相关功能
- 代数重新计算(GENERATION_RECALCULATION_FIX.md
- 始祖添加父母警告对话框(GenerationWarningDialog
- 成员删除逻辑(app/api/trees/[treeId]/members/[memberId]/route.ts