|
@@ -12,6 +12,109 @@
|
|
|
height: 60vh;
|
|
height: 60vh;
|
|
|
color: rgba(255,255,255,0.5);
|
|
color: rgba(255,255,255,0.5);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /* ── 新竖列布局 ── */
|
|
|
|
|
+ .lineage-view {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ min-width: fit-content;
|
|
|
|
|
+ padding: 10px 30px 30px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 每一行:中心列 + 右侧兄弟列 */
|
|
|
|
|
+ .lin-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ min-width: fit-content;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 中心列:固定宽度,保证所有层级垂直对齐 */
|
|
|
|
|
+ .lin-center {
|
|
|
|
|
+ min-width: 200px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 竖连接线 */
|
|
|
|
|
+ .lin-vline {
|
|
|
|
|
+ width: 3px;
|
|
|
|
|
+ min-height: 36px;
|
|
|
|
|
+ background: linear-gradient(to bottom, rgba(74,144,217,0.9), rgba(74,144,217,0.3));
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+ margin: 0 auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 右侧兄弟区:横线 + 节点列表 */
|
|
|
|
|
+ .lin-side {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ margin-left: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .lin-hline {
|
|
|
|
|
+ width: 28px;
|
|
|
|
|
+ height: 3px;
|
|
|
|
|
+ background: rgba(74,105,189,0.5);
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .lin-siblings {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ padding-left: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 子女排列区:第一子在左(对齐中心),其余向右 */
|
|
|
|
|
+ .lin-children {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ gap: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .lin-child-col {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .child-order-badge {
|
|
|
|
|
+ background: rgba(255,215,0,0.12);
|
|
|
|
|
+ border: 1px solid rgba(255,215,0,0.35);
|
|
|
|
|
+ color: #ffd700;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ padding: 2px 10px;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 分区标题 */
|
|
|
|
|
+ .section-divider {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ margin: 18px 0 10px;
|
|
|
|
|
+ color: rgba(255,215,0,0.6);
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+ .section-divider::after {
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 1px;
|
|
|
|
|
+ background: rgba(255,215,0,0.15);
|
|
|
|
|
+ min-width: 40px;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
.tree-container {
|
|
.tree-container {
|
|
|
padding: 20px;
|
|
padding: 20px;
|
|
@@ -308,6 +411,20 @@
|
|
|
margin-top: 4px;
|
|
margin-top: 4px;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /* 兄弟节点:稍小,低调 */
|
|
|
|
|
+ .tree-node.sibling-node {
|
|
|
|
|
+ background: rgba(74,105,189,0.2);
|
|
|
|
|
+ border-color: rgba(74,105,189,0.4);
|
|
|
|
|
+ min-width: 110px;
|
|
|
|
|
+ padding: 8px 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .tree-node.sibling-node .node-name {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .tree-node.sibling-node .node-info {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
{% endblock %}
|
|
{% endblock %}
|
|
|
|
|
|
|
@@ -587,213 +704,142 @@ async function loadLineage(memberId) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Render lineage tree
|
|
|
|
|
-function renderLineage(data) {
|
|
|
|
|
- console.log('Rendering lineage:', data);
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const generationsHtml = renderGenerations(data.generations);
|
|
|
|
|
- console.log('Generations HTML generated:', generationsHtml.length);
|
|
|
|
|
- document.getElementById('ancestorsTree').innerHTML = generationsHtml;
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('Error rendering generations:', e);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const siblingsWithCenterHtml = renderSiblingsWithCenter(data.center, data.siblings);
|
|
|
|
|
- console.log('Siblings HTML generated:', siblingsWithCenterHtml.length);
|
|
|
|
|
- document.getElementById('siblingsTree').innerHTML = siblingsWithCenterHtml;
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('Error rendering siblings:', e);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const childrenHtml = renderChildrenTree(data.children);
|
|
|
|
|
- console.log('Children HTML generated:', childrenHtml.length);
|
|
|
|
|
- document.getElementById('childrenTree').innerHTML = childrenHtml;
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('Error rendering children:', e);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// ── 工具函数 ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
-// Render generations with ancestors and their siblings
|
|
|
|
|
-function renderGenerations(generations) {
|
|
|
|
|
- if (!generations || generations.length === 0) return '';
|
|
|
|
|
-
|
|
|
|
|
- let html = '<div class="text-center mb-6"><span class="generation-label">祖先谱系</span></div>';
|
|
|
|
|
- html += '<div class="tree-wrapper">';
|
|
|
|
|
-
|
|
|
|
|
- const reversedGenerations = [...generations].reverse();
|
|
|
|
|
-
|
|
|
|
|
- reversedGenerations.forEach((gen, index) => {
|
|
|
|
|
- if (index > 0) {
|
|
|
|
|
- html += '<div class="connection-line vertical-line"></div>';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const leftSiblings = gen.siblings.slice(0, Math.floor(gen.siblings.length / 2));
|
|
|
|
|
- const rightSiblings = gen.siblings.slice(Math.floor(gen.siblings.length / 2));
|
|
|
|
|
-
|
|
|
|
|
- html += `
|
|
|
|
|
- <div class="generation-row">
|
|
|
|
|
- <div class="children-container">
|
|
|
|
|
- ${leftSiblings.map(sibling => `
|
|
|
|
|
- <div class="child-group">
|
|
|
|
|
- <div class="connection-line horizontal"></div>
|
|
|
|
|
- ${renderTreeNode(sibling, false, false)}
|
|
|
|
|
- ${sibling.has_children ? `
|
|
|
|
|
- <button class="expand-btn" onclick="toggleChildren(this, ${sibling.id})">+</button>
|
|
|
|
|
- <div class="children-container" style="display: none;" data-parent-id="${sibling.id}">
|
|
|
|
|
- </div>
|
|
|
|
|
- ` : ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- `).join('')}
|
|
|
|
|
- <div class="child-group direct-line">
|
|
|
|
|
- <div class="connection-line horizontal main-line"></div>
|
|
|
|
|
- ${renderTreeNode(gen.ancestor, false, true)}
|
|
|
|
|
- ${gen.ancestor.show_expand ? `
|
|
|
|
|
- <button class="expand-btn" onclick="toggleChildren(this, ${gen.ancestor.id})">+</button>
|
|
|
|
|
- <div class="children-container" style="display: none;" data-parent-id="${gen.ancestor.id}">
|
|
|
|
|
- </div>
|
|
|
|
|
- ` : ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- ${rightSiblings.map(sibling => `
|
|
|
|
|
- <div class="child-group">
|
|
|
|
|
- <div class="connection-line horizontal"></div>
|
|
|
|
|
- ${renderTreeNode(sibling, false, false)}
|
|
|
|
|
- ${sibling.has_children ? `
|
|
|
|
|
- <button class="expand-btn" onclick="toggleChildren(this, ${sibling.id})">+</button>
|
|
|
|
|
- <div class="children-container" style="display: none;" data-parent-id="${sibling.id}">
|
|
|
|
|
- </div>
|
|
|
|
|
- ` : ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- `).join('')}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- `;
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- html += '</div>';
|
|
|
|
|
- return html;
|
|
|
|
|
|
|
+// 根据 child_order 生成"长子/次子/三子..."标签
|
|
|
|
|
+function getChildOrderLabel(childOrder, fallbackIndex) {
|
|
|
|
|
+ const ord = childOrder != null ? childOrder : (fallbackIndex + 1);
|
|
|
|
|
+ const labels = ['长', '次', '三', '四', '五', '六', '七', '八', '九', '十'];
|
|
|
|
|
+ if (ord >= 1 && ord <= 10) return labels[ord - 1] + '子';
|
|
|
|
|
+ return `第${ord}子`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Render center person
|
|
|
|
|
-function renderCenterPerson(person) {
|
|
|
|
|
|
|
+// 渲染单个节点 HTML
|
|
|
|
|
+// type: 'center' | 'ancestor' | 'sibling' | 'child'
|
|
|
|
|
+function renderNode(person, type) {
|
|
|
|
|
+ if (!person) return '';
|
|
|
|
|
+ let cls = 'tree-node clickable';
|
|
|
|
|
+ if (type === 'center') cls += ' center';
|
|
|
|
|
+ else if (type === 'ancestor') cls += ' direct-ancestor';
|
|
|
|
|
+ // siblings / children use default style (can be slightly smaller)
|
|
|
|
|
+ if (type === 'sibling') cls += ' sibling-node';
|
|
|
|
|
+
|
|
|
|
|
+ if (person.sub_relation_type === 2) cls += ' adopted-out';
|
|
|
|
|
+ else if (person.sub_relation_type === 3) cls += ' adopted-in';
|
|
|
|
|
+
|
|
|
|
|
+ const adoptLabel = person.sub_relation_type === 2
|
|
|
|
|
+ ? `<div class="adoption-label">${person.adoptive_parent_name ? '出继给 ' + person.adoptive_parent_name : '出继'}</div>`
|
|
|
|
|
+ : '';
|
|
|
|
|
+
|
|
|
return `
|
|
return `
|
|
|
- <div class="connection-line"></div>
|
|
|
|
|
- <div class="tree-node center">
|
|
|
|
|
|
|
+ <div class="${cls}" data-id="${person.id}" onclick="openPersonDetail(${person.id})">
|
|
|
<div class="node-name">${person.name}</div>
|
|
<div class="node-name">${person.name}</div>
|
|
|
- ${person.simplified_name && person.simplified_name !== person.name ? `<div class="node-name simplified">(${person.simplified_name})</div>` : ''}
|
|
|
|
|
|
|
+ ${person.simplified_name && person.simplified_name !== person.name
|
|
|
|
|
+ ? `<div class="node-name simplified">(${person.simplified_name})</div>` : ''}
|
|
|
<div class="node-info">
|
|
<div class="node-info">
|
|
|
- ${person.name_word ? `${person.name_word} · ` : ''}
|
|
|
|
|
- ${person.name_word_generation || ''}
|
|
|
|
|
|
|
+ ${person.name_word ? person.name_word + ' · ' : ''}${person.name_word_generation || ''}
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- `;
|
|
|
|
|
|
|
+ ${adoptLabel}
|
|
|
|
|
+ </div>`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Render siblings with center person in the middle
|
|
|
|
|
-function renderSiblingsWithCenter(center, siblings) {
|
|
|
|
|
- const allSiblings = siblings || [];
|
|
|
|
|
-
|
|
|
|
|
- // Insert center person at the middle position
|
|
|
|
|
- const middleIndex = Math.floor(allSiblings.length / 2);
|
|
|
|
|
- const items = [];
|
|
|
|
|
-
|
|
|
|
|
- for (let i = 0; i < allSiblings.length; i++) {
|
|
|
|
|
- if (i === middleIndex) {
|
|
|
|
|
- items.push({ type: 'center', person: center });
|
|
|
|
|
- }
|
|
|
|
|
- items.push({ type: 'sibling', person: allSiblings[i] });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // If no siblings, just show center
|
|
|
|
|
- if (allSiblings.length === 0) {
|
|
|
|
|
- items.push({ type: 'center', person: center });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+// 渲染兄弟列表(横向,带横线连接)
|
|
|
|
|
+function renderSiblingsList(siblings) {
|
|
|
|
|
+ if (!siblings || siblings.length === 0) return '';
|
|
|
|
|
+ const MAX_SHOW = 6;
|
|
|
|
|
+ const shown = siblings.slice(0, MAX_SHOW);
|
|
|
|
|
+ const more = siblings.length - MAX_SHOW;
|
|
|
return `
|
|
return `
|
|
|
- <div class="text-center mt-6 mb-4"><span class="generation-label">同辈兄弟姐妹</span></div>
|
|
|
|
|
- <div class="generation-row">
|
|
|
|
|
- <div class="children-container">
|
|
|
|
|
- ${items.map(item => `
|
|
|
|
|
- <div class="child-group ${item.type === 'center' ? 'center-child' : ''}">
|
|
|
|
|
- <div class="connection-line horizontal"></div>
|
|
|
|
|
- ${renderTreeNode(item.person, item.type === 'center')}
|
|
|
|
|
- ${item.type !== 'center' && item.person.has_children ? `
|
|
|
|
|
- <button class="expand-btn" onclick="toggleChildren(this, ${item.person.id})">+</button>
|
|
|
|
|
- <div class="children-container" style="display: none;" data-parent-id="${item.person.id}">
|
|
|
|
|
- </div>
|
|
|
|
|
- ` : ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- `).join('')}
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="lin-side">
|
|
|
|
|
+ <div class="lin-hline"></div>
|
|
|
|
|
+ <div class="lin-siblings">
|
|
|
|
|
+ ${shown.map(s => renderNode(s, 'sibling')).join('')}
|
|
|
|
|
+ ${more > 0 ? `<div class="tree-node sibling-node" style="opacity:.7;">+${more} 人</div>` : ''}
|
|
|
</div>
|
|
</div>
|
|
|
- `;
|
|
|
|
|
|
|
+ </div>`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Render children tree
|
|
|
|
|
-function renderChildrenTree(children) {
|
|
|
|
|
- if (!children || children.length === 0) return '';
|
|
|
|
|
-
|
|
|
|
|
- return `
|
|
|
|
|
- <div class="text-center mt-6 mb-4"><span class="generation-label">后代谱系</span></div>
|
|
|
|
|
- <div class="generation-row">
|
|
|
|
|
- ${renderChildrenRecursive(children)}
|
|
|
|
|
- </div>
|
|
|
|
|
- `;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// ── 主渲染函数 ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
-// Render children recursively
|
|
|
|
|
-function renderChildrenRecursive(children, level = 0) {
|
|
|
|
|
- if (!children || children.length === 0) return '';
|
|
|
|
|
-
|
|
|
|
|
- return `
|
|
|
|
|
- <div class="children-container">
|
|
|
|
|
- ${children.map(child => `
|
|
|
|
|
- <div class="child-group">
|
|
|
|
|
- <div class="connection-line"></div>
|
|
|
|
|
- ${renderTreeNode(child, false)}
|
|
|
|
|
- ${child.has_children ? `
|
|
|
|
|
- <button class="expand-btn" onclick="toggleChildren(this, ${child.id})">+</button>
|
|
|
|
|
- <div class="children-container" style="display: none;" data-parent-id="${child.id}">
|
|
|
|
|
- </div>
|
|
|
|
|
- ` : ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- `).join('')}
|
|
|
|
|
- </div>
|
|
|
|
|
- `;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+function renderLineage(data) {
|
|
|
|
|
+ const { center, generations, siblings, children } = data;
|
|
|
|
|
+ const ancestorGens = [...generations].reverse(); // 从最远祖先 → 父亲
|
|
|
|
|
|
|
|
-// Render tree node
|
|
|
|
|
-function renderTreeNode(person, isCenter = false, isDirectLine = false) {
|
|
|
|
|
- let className = 'clickable ';
|
|
|
|
|
- if (isCenter) className += 'center';
|
|
|
|
|
- else if (isDirectLine) className += 'direct-ancestor';
|
|
|
|
|
- else className = className.trim();
|
|
|
|
|
-
|
|
|
|
|
- // Add adoption styles
|
|
|
|
|
- if (person.sub_relation_type === 2) {
|
|
|
|
|
- className += ' adopted-out'; // 出继
|
|
|
|
|
- } else if (person.sub_relation_type === 3) {
|
|
|
|
|
- className += ' adopted-in'; // 入继
|
|
|
|
|
|
|
+ let html = '<div class="lineage-view">';
|
|
|
|
|
+
|
|
|
|
|
+ // ── 1. 祖先竖列(最远→父亲)────────────────────────────────────────────
|
|
|
|
|
+ if (ancestorGens.length > 0) {
|
|
|
|
|
+ html += `<div class="section-divider">祖先世系</div>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ ancestorGens.forEach((gen, idx) => {
|
|
|
|
|
+ // 竖连接线(第一个不加顶部线,后面每个加)
|
|
|
|
|
+ if (idx > 0) {
|
|
|
|
|
+ html += `<div class="lin-row"><div class="lin-center"><div class="lin-vline"></div></div></div>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ html += `<div class="lin-row">`;
|
|
|
|
|
+ html += ` <div class="lin-center">${renderNode(gen.ancestor, 'ancestor')}</div>`;
|
|
|
|
|
+ // 该祖先的兄弟(向右展示)
|
|
|
|
|
+ html += renderSiblingsList(gen.siblings || []);
|
|
|
|
|
+ html += `</div>`;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // ── 2. 中心人物 ──────────────────────────────────────────────────────────
|
|
|
|
|
+ // 连接线
|
|
|
|
|
+ if (ancestorGens.length > 0) {
|
|
|
|
|
+ html += `<div class="lin-row"><div class="lin-center"><div class="lin-vline"></div></div></div>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ html += `<div class="section-divider">查询人物</div>`;
|
|
|
|
|
+ html += `<div class="lin-row">`;
|
|
|
|
|
+ html += ` <div class="lin-center">${renderNode(center, 'center')}</div>`;
|
|
|
|
|
+ // 中心人物的兄弟姐妹(向右展示)
|
|
|
|
|
+ html += renderSiblingsList(siblings || []);
|
|
|
|
|
+ html += `</div>`;
|
|
|
|
|
+
|
|
|
|
|
+ // ── 3. 子女横排(第一子对齐,其余向右)──────────────────────────────────
|
|
|
|
|
+ if (children && children.length > 0) {
|
|
|
|
|
+ html += `<div class="lin-row"><div class="lin-center"><div class="lin-vline"></div></div></div>`;
|
|
|
|
|
+ html += `<div class="section-divider">子女</div>`;
|
|
|
|
|
+ html += `<div class="lin-row">`;
|
|
|
|
|
+ html += ` <div class="lin-children">`;
|
|
|
|
|
+ children.forEach((child, idx) => {
|
|
|
|
|
+ const badge = getChildOrderLabel(child.child_order, idx);
|
|
|
|
|
+ html += `<div class="lin-child-col">`;
|
|
|
|
|
+ html += ` <div class="child-order-badge">${badge}</div>`;
|
|
|
|
|
+ html += renderNode(child, 'child');
|
|
|
|
|
+ if (child.has_children) {
|
|
|
|
|
+ html += `<button class="expand-btn" onclick="toggleChildren(this,${child.id})">+</button>`;
|
|
|
|
|
+ html += `<div class="children-container" style="display:none;" data-parent-id="${child.id}"></div>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ html += `</div>`;
|
|
|
|
|
+ });
|
|
|
|
|
+ html += ` </div>`;
|
|
|
|
|
+ html += `</div>`;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 出继方显示"出继给xxx"
|
|
|
|
|
- const adoptionLabel = person.sub_relation_type === 2 && person.adoptive_parent_name
|
|
|
|
|
- ? `<div class="adoption-label">出继给 ${person.adoptive_parent_name}</div>`
|
|
|
|
|
- : (person.sub_relation_type === 2 ? '<div class="adoption-label">出继</div>' : '');
|
|
|
|
|
-
|
|
|
|
|
- return `
|
|
|
|
|
- <div class="tree-node ${className}" data-id="${person.id}" onclick="openPersonDetail(${person.id})">
|
|
|
|
|
- <div class="node-name">${person.name}</div>
|
|
|
|
|
- ${person.simplified_name && person.simplified_name !== person.name ? `<div class="node-name simplified">(${person.simplified_name})</div>` : ''}
|
|
|
|
|
- <div class="node-info">
|
|
|
|
|
- ${person.name_word ? `${person.name_word} · ` : ''}
|
|
|
|
|
- ${person.name_word_generation || ''}
|
|
|
|
|
- </div>
|
|
|
|
|
- ${adoptionLabel}
|
|
|
|
|
- </div>
|
|
|
|
|
- `;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ html += '</div>'; // lineage-view
|
|
|
|
|
+
|
|
|
|
|
+ // 写入容器(ancestorsTree 承载全部内容,其余清空)
|
|
|
|
|
+ document.getElementById('ancestorsTree').innerHTML = html;
|
|
|
|
|
+ document.getElementById('siblingsTree').innerHTML = '';
|
|
|
|
|
+ document.getElementById('childrenTree').innerHTML = '';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 展开子孙(按钮旁的懒加载容器)
|
|
|
|
|
+function renderChildrenRecursive(children) {
|
|
|
|
|
+ if (!children || children.length === 0) return '';
|
|
|
|
|
+ return `<div class="lin-children" style="flex-wrap:wrap;gap:12px;">
|
|
|
|
|
+ ${children.map((child, idx) => {
|
|
|
|
|
+ const badge = getChildOrderLabel(child.child_order, idx);
|
|
|
|
|
+ return `<div class="lin-child-col">
|
|
|
|
|
+ <div class="child-order-badge">${badge}</div>
|
|
|
|
|
+ ${renderNode(child, 'child')}
|
|
|
|
|
+ ${child.has_children
|
|
|
|
|
+ ? `<button class="expand-btn" onclick="toggleChildren(this,${child.id})">+</button>
|
|
|
|
|
+ <div class="children-container" style="display:none;" data-parent-id="${child.id}"></div>`
|
|
|
|
|
+ : ''}
|
|
|
|
|
+ </div>`;
|
|
|
|
|
+ }).join('')}
|
|
|
|
|
+ </div>`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Open person detail in new tab
|
|
// Open person detail in new tab
|