// 全局元素索引(将由Python动态注入)
// const elementIndex = {};
// 卡片展开/收起功能
function toggleCardDetails(cardId) {
const detailsElement = document.getElementById(cardId + '-details');
const cardElement = document.querySelector('[data-card-id="' + cardId + '"]');
if (!detailsElement || !cardElement) {
return;
}
// 切换显示/隐藏
if (detailsElement.style.display === 'none' || !detailsElement.style.display) {
detailsElement.style.display = 'block';
cardElement.classList.add('expanded');
// 更新图标
const toggleIcon = cardElement.querySelector('.toggle-icon');
if (toggleIcon) {
toggleIcon.textContent = '▲';
}
} else {
detailsElement.style.display = 'none';
cardElement.classList.remove('expanded');
// 更新图标
const toggleIcon = cardElement.querySelector('.toggle-icon');
if (toggleIcon) {
toggleIcon.textContent = '▼';
}
}
}
// 显示元素详情模态框
function showElementDetail(elementId) {
const elem = elementIndex[elementId];
if (!elem) return;
// 创建模态框
const modal = document.createElement('div');
modal.className = 'element-modal-backdrop';
modal.onclick = function(e) {
if (e.target === modal) {
document.body.removeChild(modal);
}
};
// 创建模态框内容
const modalContent = document.createElement('div');
modalContent.className = 'element-modal-content';
modalContent.onclick = function(e) {
e.stopPropagation();
};
// 构建内容
let html = '
';
html += '';
// 描述
if (elem.description) {
html += '
';
html += '
描述:';
html += '
' + elem.description + '
';
html += '
';
}
// 类型和维度
html += '
';
html += '类型: ' + elem.type;
if (elem.dimension && elem.dimension.一级) {
html += ' / ' + elem.dimension.一级;
if (elem.dimension.二级) {
html += ' / ' + elem.dimension.二级;
}
}
html += '
';
// 分类
if (elem.category) {
html += '
';
html += '分类:';
if (typeof elem.category === 'object') {
html += ' ' + (elem.category.一级分类 || '');
if (elem.category.二级分类) {
html += ' / ' + elem.category.二级分类;
}
} else {
html += ' ' + elem.category;
}
html += '
';
}
// 跳转到Tab3查看完整信息
html += '';
html += '
';
modalContent.innerHTML = html;
modal.appendChild(modalContent);
document.body.appendChild(modal);
}
// 跳转到元素详情(Tab3)
function jumpToElement(elementId) {
// 关闭模态框
const modal = document.querySelector('.element-modal-backdrop');
if (modal) {
modal.remove();
}
// 切换到Tab3
switchTab('tab3');
// 等待DOM更新后滚动到元素
setTimeout(function() {
const elemItem = document.querySelector('[data-elem-id="' + elementId + '"]');
if (elemItem) {
// 展开所有父级容器
let parent = elemItem.parentElement;
while (parent) {
if (parent.classList.contains('collapsed')) {
parent.classList.remove('collapsed');
}
parent = parent.parentElement;
}
// 滚动到元素并高亮
elemItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
elemItem.classList.add('highlight-pulse');
setTimeout(function() {
elemItem.classList.remove('highlight-pulse');
}, 2000);
}
}, 100);
}
// Tab切换功能
function switchTab(tabId) {
// 隐藏所有tab内容
const allTabs = document.querySelectorAll('.tab-content');
allTabs.forEach(tab => {
tab.style.display = 'none';
});
// 移除所有tab的active类
const allTabButtons = document.querySelectorAll('.tab');
allTabButtons.forEach(btn => {
btn.classList.remove('active');
});
// 显示选中的tab内容
document.getElementById(tabId).style.display = 'block';
// 给选中的tab按钮添加active类
if (event && event.target) {
event.target.classList.add('active');
} else {
// 程序化切换时,手动添加active类
document.querySelectorAll('.tab').forEach((btn, idx) => {
if ((tabId === 'tab1' && idx === 0) ||
(tabId === 'tab2' && idx === 1) ||
(tabId === 'tab3' && idx === 2) ||
(tabId === 'tab4' && idx === 3) ||
(tabId === 'tab5' && idx === 4)) {
btn.classList.add('active');
}
});
}
// 如果切换到tab4,重新绘制连线
if (tabId === 'tab4') {
setTimeout(() => {
drawAllConnections();
}, 100);
}
// Tab5 不在切换时自动绘制连线,只在点击卡片时绘制
}
// 展开/收起段落功能
function toggleCollapse(element) {
const listItem = element.closest('.paragraph-item');
if (!listItem.classList.contains('collapsible')) {
return;
}
listItem.classList.toggle('collapsed');
}
// 展开/收起段落详细内容
function toggleDetails(element) {
const detailsContainer = element.closest('.paragraph-item')
.querySelector('.paragraph-details');
if (detailsContainer) {
detailsContainer.classList.toggle('collapsed');
const toggleBtn = element.closest('.paragraph-header')
.querySelector('.details-toggle-btn');
if (detailsContainer.classList.contains('collapsed')) {
toggleBtn.innerHTML = '\n';
html += '
支撑关系
\n';
// 显示来源实质点
const substanceData = supportRelationships.substance_to_target[substanceId];
html += '
\n';
html += '
\n';
html += `
${substanceData.type || '实质点'}
\n`;
html += `
${substanceData.name || substanceId}
\n`;
html += '
\n';
html += '
\n';
// 显示支撑的目标点(分类)
html += '
\n';
if (relations.inspiration && relations.inspiration.length > 0) {
html += '
\n';
html += '
灵感点
\n';
relations.inspiration.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
if (relations.purpose && relations.purpose.length > 0) {
html += '
\n';
html += '
目的点
\n';
relations.purpose.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
if (relations.keypoint && relations.keypoint.length > 0) {
html += '
\n';
html += '
关键点
\n';
relations.keypoint.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
html += '
\n';
html += '
\n';
// 插入到画布
canvas.insertAdjacentHTML('beforeend', html);
}
// 绘制反向支撑关系(目标点 ← 实质点)
function drawReverseSupportRelationships(targetId, relations) {
const canvas = document.getElementById('support-canvas');
if (!canvas) return;
// 清空并移除占位文本
const placeholder = canvas.querySelector('.placeholder-text');
if (placeholder) {
placeholder.style.display = 'none';
}
// 创建可视化内容
let html = '\n';
html += '
支撑关系
\n';
// 显示来源形式点
html += '
\n';
html += '
\n';
html += '
\n';
// 显示支撑的目标点(分类)
html += '
\n';
if (relations.inspiration && relations.inspiration.length > 0) {
html += '
\n';
html += '
灵感点
\n';
relations.inspiration.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
if (relations.purpose && relations.purpose.length > 0) {
html += '
\n';
html += '
目的点
\n';
relations.purpose.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
if (relations.keypoint && relations.keypoint.length > 0) {
html += '
\n';
html += '
关键点
\n';
relations.keypoint.forEach(rel => {
html += `
\n`;
html += `
${rel.point}
\n`;
// 兼容新结构(支撑理由)和旧结构(相似度分数)
if (rel.support_reason !== undefined) {
html += `
支撑
\n`;
} else if (rel.avg_score !== undefined) {
const score = (rel.avg_score * 100).toFixed(0);
html += `
相似度: ${score}%
\n`;
}
html += '
\n';
});
html += '
\n';
}
if (relations.substance && relations.substance.length > 0) {
html += '
\n';
html += '
实质点
\n';
relations.substance.forEach(rel => {
const score = (rel.avg_score * 100).toFixed(0);
let flowClass = '';
if (rel.type === '具体元素') {
flowClass = 'concrete-element-flow';
} else if (rel.type === '具体概念') {
flowClass = 'concrete-concept-flow';
} else if (rel.type === '抽象概念') {
flowClass = 'abstract-concept-flow';
}
html += `
\n`;
html += `
${rel.name} (${rel.type})
\n`;
html += `
相似度: ${score}%
\n`;
html += '
\n';
});
html += '
\n';
}
html += '
\n';
html += '
\n';
// 插入到画布
canvas.insertAdjacentHTML('beforeend', html);
}
// 绘制反向形式点支撑关系(目标点 ← 形式点)
function drawReverseFormSupportRelationships(targetId, relations) {
const canvas = document.getElementById('form-support-canvas');
if (!canvas) return;
// 清空并移除占位文本
const placeholder = canvas.querySelector('.placeholder-text');
if (placeholder) {
placeholder.style.display = 'none';
}
// 创建可视化内容
let html = '\n';
html += '
被以下形式点支撑
\n';
// 显示目标点
html += '
\n';
html += '
\n';
html += `
目标点
\n`;
html += `
${targetId}
\n`;
html += '
\n';
html += '
\n';
// 显示支撑的形式点(分类)
html += '
\n';
if (relations.concrete_element_forms && relations.concrete_element_forms.length > 0) {
html += '
\n';
html += '
具体元素形式
\n';
relations.concrete_element_forms.forEach(item => {
const score = (item.score * 100).toFixed(0);
html += `
\n';
});
html += '
\n';
}
if (relations.concrete_concept_forms && relations.concrete_concept_forms.length > 0) {
html += '
\n';
html += '
具体概念形式
\n';
relations.concrete_concept_forms.forEach(item => {
const score = (item.score * 100).toFixed(0);
html += `
\n';
});
html += '
\n';
}
if (relations.overall_forms && relations.overall_forms.length > 0) {
html += '
\n';
html += '
整体形式
\n';
relations.overall_forms.forEach(item => {
const score = (item.score * 100).toFixed(0);
html += `
\n';
});
html += '
\n';
}
if (relations.forms && relations.forms.length > 0) {
html += '
\n';
html += '
形式点
\n';
relations.forms.forEach(item => {
const score = (item.score * 100).toFixed(0);
html += `
\n';
});
html += '
\n';
}
html += '
\n';
html += '
\n';
// 插入到画布
canvas.insertAdjacentHTML('beforeend', html);
}
// 清空形式点支撑关系画布
function clearFormSupportCanvas() {
const canvas = document.getElementById('form-support-canvas');
if (!canvas) return;
// 移除所有flow相关元素
const flows = canvas.querySelectorAll('.support-flow');
flows.forEach(flow => flow.remove());
// 重新显示占位文本
const placeholder = canvas.querySelector('.placeholder-text');
if (placeholder) {
placeholder.style.display = 'flex';
}
}
// ===== Tab5 连线绘制功能 =====
// 绘制Tab5所有连线
function drawFormAllConnections() {
if (typeof tab5Relationships === 'undefined') {
console.log('tab5Relationships not defined, skipping connection drawing');
return;
}
const svg = document.getElementById('tab5-connection-svg');
if (!svg) {
console.log('SVG container not found');
return;
}
// 清空现有连线
svg.innerHTML = '';
// 1. 绘制左侧选题点到实质点的连线(反向关系)
drawLeftTargetToSubstanceConnections(svg);
// 2. 绘制实质点到形式点的连线(支撑关系)
drawSubstanceToFormConnections(svg);
// 3. 绘制形式点到右侧选题点的连线
drawFormToRightTargetConnections(svg);
}
// 绘制左侧选题点到实质点的连线
function drawLeftTargetToSubstanceConnections(svg) {
const targetFromSubstance = tab5Relationships.target_from_substance;
Object.keys(targetFromSubstance).forEach(targetId => {
const substances = targetFromSubstance[targetId];
substances.forEach(substanceData => {
const sourceCard = document.querySelector(`.tab5-left-targets .target-card[data-id="${targetId}"]`);
const targetCard = document.querySelector(`.tab5-substances .substance-card[data-id="${substanceData.substance_id}"]`);
if (sourceCard && targetCard && !sourceCard.classList.contains('hidden') && !targetCard.classList.contains('hidden')) {
drawTab5Connection(
svg,
sourceCard,
targetCard,
'left-target-substance',
targetId,
substanceData.substance_id,
{
score: substanceData.score,
substanceName: substanceData.name,
type: substanceData.type
}
);
}
});
});
}
// 绘制实质点到形式点的连线
function drawSubstanceToFormConnections(svg) {
const formToSubstance = tab5Relationships.form_to_substance;
Object.keys(formToSubstance).forEach(formId => {
const substances = formToSubstance[formId];
substances.forEach(substanceData => {
const sourceCard = document.querySelector(`.tab5-substances .substance-card[data-id="${substanceData.substance_id}"]`);
const targetCard = document.querySelector(`.tab5-forms .form-card[data-id="${formId}"]`);
if (sourceCard && targetCard && !sourceCard.classList.contains('hidden') && !targetCard.classList.contains('hidden')) {
drawTab5Connection(
svg,
sourceCard,
targetCard,
'substance-form',
substanceData.substance_id,
formId,
{
substanceName: substanceData.name,
formName: tab5Relationships.form_to_target[formId]?.name || ''
}
);
}
});
});
}
// 绘制形式点到右侧选题点的连线
function drawFormToRightTargetConnections(svg) {
const formToTarget = tab5Relationships.form_to_target;
Object.keys(formToTarget).forEach(formId => {
const relations = formToTarget[formId];
// 绘制到灵感点的连线
relations.inspiration.forEach(rel => {
const sourceCard = document.querySelector(`.tab5-forms .form-card[data-id="${formId}"]`);
const targetCard = document.querySelector(`.tab5-right-targets .target-card[data-id="${rel.target_id}"]`);
if (sourceCard && targetCard && !sourceCard.classList.contains('hidden') && !targetCard.classList.contains('hidden')) {
// 兼容新结构(支撑)和旧结构(相似度分数)
const score = rel.avg_score !== undefined ? rel.avg_score : (rel.support_reason !== undefined ? 1.0 : 0.5);
drawTab5Connection(
svg,
sourceCard,
targetCard,
'form-inspiration',
formId,
rel.target_id,
{
score: score,
point: rel.point,
formName: relations.name
}
);
}
});
// 绘制到目的点的连线
relations.purpose.forEach(rel => {
const sourceCard = document.querySelector(`.tab5-forms .form-card[data-id="${formId}"]`);
const targetCard = document.querySelector(`.tab5-right-targets .target-card[data-id="${rel.target_id}"]`);
if (sourceCard && targetCard && !sourceCard.classList.contains('hidden') && !targetCard.classList.contains('hidden')) {
// 兼容新结构(支撑)和旧结构(相似度分数)
const score = rel.avg_score !== undefined ? rel.avg_score : (rel.support_reason !== undefined ? 1.0 : 0.5);
drawTab5Connection(
svg,
sourceCard,
targetCard,
'form-purpose',
formId,
rel.target_id,
{
score: score,
point: rel.point,
formName: relations.name
}
);
}
});
// 绘制到关键点的连线
relations.keypoint.forEach(rel => {
const sourceCard = document.querySelector(`.tab5-forms .form-card[data-id="${formId}"]`);
const targetCard = document.querySelector(`.tab5-right-targets .target-card[data-id="${rel.target_id}"]`);
if (sourceCard && targetCard && !sourceCard.classList.contains('hidden') && !targetCard.classList.contains('hidden')) {
// 兼容新结构(支撑)和旧结构(相似度分数)
const score = rel.avg_score !== undefined ? rel.avg_score : (rel.support_reason !== undefined ? 1.0 : 0.5);
drawTab5Connection(
svg,
sourceCard,
targetCard,
'form-keypoint',
formId,
rel.target_id,
{
score: score,
point: rel.point,
formName: relations.name
}
);
}
});
});
}
// 绘制单条Tab5连线
function drawTab5Connection(svg, sourceCard, targetCard, connectionType, sourceId, targetId, metadata) {
const svgContainer = svg.parentElement;
const containerRect = svgContainer.getBoundingClientRect();
const sourceRect = sourceCard.getBoundingClientRect();
const targetRect = targetCard.getBoundingClientRect();
// 计算连线起点和终点
let x1, y1, x2, y2;
if (connectionType === 'left-target-substance') {
// 左侧选题点到实质点:从右边连到左边
x1 = sourceRect.right - containerRect.left;
y1 = sourceRect.top + sourceRect.height / 2 - containerRect.top;
x2 = targetRect.left - containerRect.left;
y2 = targetRect.top + targetRect.height / 2 - containerRect.top;
} else if (connectionType === 'substance-form') {
// 实质点到形式点:从右边连到左边
x1 = sourceRect.right - containerRect.left;
y1 = sourceRect.top + sourceRect.height / 2 - containerRect.top;
x2 = targetRect.left - containerRect.left;
y2 = targetRect.top + targetRect.height / 2 - containerRect.top;
} else {
// 形式点到右侧选题点:从右边连到左边
x1 = sourceRect.right - containerRect.left;
y1 = sourceRect.top + sourceRect.height / 2 - containerRect.top;
x2 = targetRect.left - containerRect.left;
y2 = targetRect.top + targetRect.height / 2 - containerRect.top;
}
// 创建贝塞尔曲线路径
const midX = (x1 + x2) / 2;
const path = `M ${x1} ${y1} Q ${midX} ${y1}, ${midX} ${(y1 + y2) / 2} T ${x2} ${y2}`;
// 创建path元素
const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathElement.setAttribute('d', path);
pathElement.setAttribute('class', `tab5-connection-line ${connectionType}`);
pathElement.setAttribute('fill', 'none');
pathElement.setAttribute('stroke-width', '2');
pathElement.setAttribute('data-source-id', sourceId);
pathElement.setAttribute('data-target-id', targetId);
pathElement.setAttribute('data-connection-type', connectionType);
// 保存元数据以供点击时使用
pathElement.setAttribute('data-metadata', JSON.stringify(metadata));
// 添加点击事件
pathElement.style.cursor = 'pointer';
pathElement.addEventListener('click', function(e) {
e.stopPropagation();
showConnectionDetail(connectionType, sourceId, targetId, metadata);
});
// 添加hover效果
pathElement.addEventListener('mouseenter', function() {
this.setAttribute('stroke-width', '4');
this.style.filter = 'drop-shadow(0 0 6px rgba(0,0,0,0.3))';
});
pathElement.addEventListener('mouseleave', function() {
this.setAttribute('stroke-width', '2');
this.style.filter = 'none';
});
svg.appendChild(pathElement);
}
// 显示连线详情模态框
function showConnectionDetail(connectionType, sourceId, targetId, metadata) {
// 创建模态框
const modal = document.createElement('div');
modal.className = 'connection-modal-backdrop';
modal.onclick = function(e) {
if (e.target === modal) {
document.body.removeChild(modal);
}
};
// 创建模态框内容
const modalContent = document.createElement('div');
modalContent.className = 'connection-modal-content';
modalContent.onclick = function(e) {
e.stopPropagation();
};
// 构建内容
let html = '';
html += '';
// 根据连接类型显示不同内容
if (connectionType === 'left-target-substance') {
html += '
';
html += '
连接类型:
';
html += '
选题点 → 实质点
';
html += '
';
html += '
';
html += '
实质点:
';
html += '
' + (metadata.substanceName || sourceId) + '
';
html += '
';
html += '
';
html += '
实质点类型:
';
html += '
' + (metadata.type || '-') + '
';
html += '
';
if (metadata.score !== undefined) {
const scorePercent = (metadata.score * 100).toFixed(1);
html += '
';
html += '
相似度评分:
';
html += '
' + scorePercent + '%
';
html += '
';
}
html += '
';
html += '
说明:
';
html += '
该实质点支撑了这个选题点的内容,相似度越高表示关联性越强
';
html += '
';
} else if (connectionType === 'substance-form') {
html += '
';
html += '
连接类型:
';
html += '
实质点 → 形式点
';
html += '
';
html += '
';
html += '
实质点:
';
html += '
' + (metadata.substanceName || sourceId) + '
';
html += '
';
html += '
';
html += '
形式点:
';
html += '
' + (metadata.formName || targetId) + '
';
html += '
';
html += '
';
html += '
说明:
';
html += '
该形式点是对实质点的具体表现形式
';
html += '
';
} else if (connectionType.startsWith('form-')) {
const targetType = connectionType.split('-')[1]; // inspiration, purpose, keypoint
const targetTypeLabel = {
'inspiration': '灵感点',
'purpose': '目的点',
'keypoint': '关键点'
}[targetType] || targetType;
html += '
';
html += '
连接类型:
';
html += '
形式点 → ' + targetTypeLabel + '
';
html += '
';
html += '
';
html += '
形式点:
';
html += '
' + (metadata.formName || sourceId) + '
';
html += '
';
if (metadata.point) {
html += '
';
html += '
' + targetTypeLabel + ':
';
html += '
' + metadata.point + '
';
html += '
';
}
if (metadata.score !== undefined) {
const scorePercent = (metadata.score * 100).toFixed(1);
html += '
';
html += '
相似度评分:
';
html += '
' + scorePercent + '%
';
html += '
';
}
html += '
';
html += '
说明:
';
html += '
该形式点支撑了这个选题点的呈现,相似度越高表示形式与选题的契合度越高
';
html += '
';
}
html += '
';
modalContent.innerHTML = html;
modal.appendChild(modalContent);
document.body.appendChild(modal);
}