0.8.0.0
This commit is contained in:
@@ -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` 来判断始祖
|
||||
|
||||
### 原因2:findNewRootMember 按创建时间排序
|
||||
`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)
|
||||
Reference in New Issue
Block a user