This commit is contained in:
itcsx committed 2026-09-15 08:57:54 +08:00
1 parent be6d24e096
commit f6157578de
2 files changed
+54 -4

No files matched your search

@@ -69,10 +69,12 @@ const data = ref<FamilyTreeData | null>(null);
const hasPerson = computed(() => !!data.value?.CurrentPerson?.FamilyPerson?.PersonId);
const rootPersonId = computed(() => data.value?.CurrentPerson?.FamilyPerson?.PersonId ?? "");
/** 全族索引:人员信息 / 夫妻关系(双向)/ 亲子关系 */
/** 全族索引:人员信息 / 夫妻关系(双向)/ 亲子关系 / 亲子反向索引 */
let personMap = new Map<string, any>();
let spousesOf = new Map<string, any[]>();
let childrenOf = new Map<string, any[]>();
/** 孩子 -> 父母记录列表(一个孩子可能同时挂在生父、过继父等多个父节点下) */
let parentsOf = new Map<string, { parentId: string; relation?: string }[]>();
/** 已展开(显示孩子)的节点 personId 集合 */
const expandedIds = ref<Set<string>>(new Set());
@@ -155,6 +157,7 @@ const buildIndex = (resp: FamilyTreeData) => {
personMap = new Map();
spousesOf = new Map();
childrenOf = new Map();
parentsOf = new Map();
// 1) 所有人员信息(含性别),优先作为人员卡片数据源
const persons = resp?.CurrentPerson?.FamilyTreePersons ?? [];
@@ -238,10 +241,29 @@ const buildIndex = (resp: FamilyTreeData) => {
orderby: c.Orderby,
});
childrenOf.set(c.PersonId, list);
// 反向记录:同一孩子可能有多条父记录(生父 / 过继父),保留以判断由谁承担后代
const parentList = parentsOf.get(c.ChildrenId) ?? [];
parentList.push({ parentId: c.PersonId, relation: c.RelationType });
parentsOf.set(c.ChildrenId, parentList);
}
});
};
/** 承祧 / 入嗣 / 收养类关系:孩子被过继到该父名下,由其承担其后代世系 */
const ADOPTIVE_RELATIONS = new Set(["C", "E", "F", "G", "H", "I", "J", "K", "L"]);
/**
* 同一孩子挂在多个父节点下时,选定“承担后代”的父节点:
* 优先取承祧 / 过继关系的一方(如“继子”“承嗣”),否则取记录顺序中的第一个。
* 其余父节点仅显示该孩子的引用卡片,不再展开其后代,避免后代被重复展示。
*/
const primaryParentOf = (personId: string): string => {
const parents = parentsOf.get(personId) ?? [];
if (parents.length === 0) return "";
const adopted = parents.find((p) => ADOPTIVE_RELATIONS.has(p.relation ?? ""));
return (adopted ?? parents[0]).parentId;
};
/** 构建图数据:递归一次性展示所有人员,祖先为根,配偶同行右侧,孩子列在配偶正下方 */
const buildGraphData = () => {
const nodes: any[] = [];
@@ -320,8 +342,36 @@ const buildGraphData = () => {
// 孩子列与本人第一行的配偶列对齐(x + 260),即孩子位于配偶正下方,竖直跟第一行对齐
const childBaseX = x + 260;
children.forEach((c: any) => {
walk(c.id, depth + 1, childBaseX);
edges.push({ source: "n-" + personId, target: "n-" + c.id, data: { kind: "parent" } });
// 一个孩子可能同时是多方之子(生父 / 过继父):每个父节点下各显示一张卡片。
// 由“承祧 / 过继”的一方承担其后代,其余父节点只显示一张引用卡片(不展开其后代)。
const isPrimary = primaryParentOf(c.id) === personId;
if (isPrimary) {
if (!visited.has(c.id)) {
walk(c.id, depth + 1, childBaseX);
}
edges.push({ source: "n-" + personId, target: "n-" + c.id, data: { kind: "parent" } });
} else {
const childInfo = personMap.get(c.id) ?? {};
const refId = "r-" + personId + "-" + c.id;
nodes.push({
id: refId,
style: { x: childBaseX, y: cursor },
data: {
kind: "member",
personId: c.id,
tag: "族员",
name: (childInfo.Surname ?? c.ChildrenSurname ?? "") + (childInfo.GenealogyName ?? c.ChildrenGenealogyName ?? ""),
commonName: childInfo.CommonName ?? c.ChildrenCommonName,
gender: childInfo.Gender ?? c.gender,
avatarUrl: childInfo.AvatarUrl ?? "",
hasChildren: false,
expanded: false,
loaded: true,
},
});
cursor += Y_STEP;
edges.push({ source: "n-" + personId, target: refId, data: { kind: "parent" } });
}
});
}
};