This commit is contained in:
freedakgmail
2025-12-21 17:32:33 +08:00
parent cffdd75e96
commit 5c8c70097c
190 changed files with 2746 additions and 893 deletions
+174
View File
@@ -0,0 +1,174 @@
# 始祖显示和删除后重新定位修复
## 问题描述
### 问题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