|
|
|
|
@ -489,24 +489,33 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 知识图谱视图 -->
|
|
|
|
|
<div id="knowledge-graph-view" class="view-container hidden flex flex-col h-full">
|
|
|
|
|
<div class="bg-white p-4 border-b border-gray-200">
|
|
|
|
|
<!-- 知识图谱视图 -->
|
|
|
|
|
<div id="knowledge-graph-view" class="view-container hidden flex flex-col h-full relative">
|
|
|
|
|
<!-- 主内容区:图谱展示 -->
|
|
|
|
|
<div class="bg-white p-2 border-b border-gray-200">
|
|
|
|
|
<h1 class="text-xl font-semibold">知识图谱</h1>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 参数配置区域 -->
|
|
|
|
|
<div class="p-2 bg-white border-b border-gray-200"> <!-- 可选:将p-6改为p-4 -->
|
|
|
|
|
<form id="knowledgeGraphForm" class="space-y-2">
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2"> <!-- 将gap-6改为gap-4 -->
|
|
|
|
|
<!-- 图谱展示区域 -->
|
|
|
|
|
<div class="flex-1 overflow-hidden flex flex-col">
|
|
|
|
|
<div id="graph-container" class="flex-1 border-b border-gray-200"></div>
|
|
|
|
|
<div id="node-info" class="p-4 bg-white text-sm" style="height: 200px; overflow-y: auto;">
|
|
|
|
|
<p>请点击"生成知识图谱"按钮加载数据</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 右侧悬浮参数面板 -->
|
|
|
|
|
<div class="absolute top-4 right-4 w-72 bg-white rounded-lg shadow-lg border border-gray-200 p-3 z-10">
|
|
|
|
|
<h3 class="text-sm font-semibold mb-2 text-gray-700">参数配置</h3>
|
|
|
|
|
<form id="knowledgeGraphForm" class="space-y-2">
|
|
|
|
|
<div>
|
|
|
|
|
<label for="graphLabel" class="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
<label for="graphLabel" class="block text-xs font-medium text-gray-600 mb-1">
|
|
|
|
|
标签 <span class="text-red-500">*</span>
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="graphLabel"
|
|
|
|
|
class="input-field w-full"
|
|
|
|
|
class="input-field w-full text-sm"
|
|
|
|
|
placeholder="输入标签(支持通配符*)"
|
|
|
|
|
required
|
|
|
|
|
value="*"
|
|
|
|
|
@ -514,65 +523,54 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label for="graphMaxDepth" class="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
<label for="graphMaxDepth" class="block text-xs font-medium text-gray-600 mb-1">
|
|
|
|
|
最大深度
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
id="graphMaxDepth"
|
|
|
|
|
class="input-field w-full"
|
|
|
|
|
class="input-field w-full text-sm"
|
|
|
|
|
value="3"
|
|
|
|
|
min="1"
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label for="graphMaxNodes" class="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
<label for="graphMaxNodes" class="block text-xs font-medium text-gray-600 mb-1">
|
|
|
|
|
最大节点数
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
id="graphMaxNodes"
|
|
|
|
|
class="input-field w-full"
|
|
|
|
|
class="input-field w-full text-sm"
|
|
|
|
|
value="100"
|
|
|
|
|
min="1"
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 替换原来的文档ID输入框 -->
|
|
|
|
|
<div>
|
|
|
|
|
<label for="graphCategory" class="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
分类 <span class="text-red-500">*</span>
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
id="graphCategory"
|
|
|
|
|
class="input-field w-full"
|
|
|
|
|
required
|
|
|
|
|
>
|
|
|
|
|
<option value="">请选择分类</option>
|
|
|
|
|
<!-- 动态生成分类选项 -->
|
|
|
|
|
</select>
|
|
|
|
|
<p class="text-xs text-gray-500 mt-1">选择分类后,将生成该分类下所有文件的知识图谱</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex justify-end pt-1">
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
class="btn-primary bg-blue-600 text-white px-4 py-1.5 rounded-lg flex items-center hover:bg-blue-700 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<i class="fa fa-sitemap mr-1"></i> 生成知识图谱
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 图谱展示区域 -->
|
|
|
|
|
<div class="flex-1 overflow-hidden flex flex-col">
|
|
|
|
|
<div id="graph-container" class="flex-1 border-b border-gray-200"></div>
|
|
|
|
|
<div id="node-info" class="p-4 bg-white text-sm">
|
|
|
|
|
<p>请点击"生成知识图谱"按钮加载数据</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label for="graphCategory" class="block text-xs font-medium text-gray-600 mb-1">
|
|
|
|
|
分类 <span class="text-red-500">*</span>
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
id="graphCategory"
|
|
|
|
|
class="input-field w-full text-sm"
|
|
|
|
|
required
|
|
|
|
|
>
|
|
|
|
|
<option value="">请选择分类</option>
|
|
|
|
|
<!-- 动态生成分类选项 -->
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="pt-2">
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
class="w-full bg-blue-600 text-white px-3 py-1.5 rounded-lg text-sm flex items-center justify-center hover:bg-blue-700 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<i class="fa fa-sitemap mr-1"></i> 生成知识图谱
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</main>
|
|
|
|
|
@ -1474,7 +1472,7 @@ function fetchNotesFromServer() {
|
|
|
|
|
const validNotes = Array.isArray(data.notes) ? data.notes : [];
|
|
|
|
|
notesData = validNotes;
|
|
|
|
|
localStorage.setItem('notesData', JSON.stringify(notesData));
|
|
|
|
|
renderNotes();
|
|
|
|
|
// renderNotes();
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
@ -2054,13 +2052,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 转换为ECharts所需格式
|
|
|
|
|
const echartsNodes = kgData.nodes.map(node => ({
|
|
|
|
|
id: node.id,
|
|
|
|
|
name: node.labels?.[0] || '未知节点',
|
|
|
|
|
category: node.properties?.entity_type || '默认类型',
|
|
|
|
|
properties: node.properties || {},
|
|
|
|
|
symbolSize: 20 + (nodeLinkCount[node.id] * 3)
|
|
|
|
|
}));
|
|
|
|
|
const echartsNodes = kgData.nodes.map(node => {
|
|
|
|
|
const linkCount = nodeLinkCount[node.id];
|
|
|
|
|
// 对数增长公式:基础大小 + 对数增长部分(增长越来越慢)
|
|
|
|
|
// Math.log1p(linkCount) 等价于 Math.log(1 + linkCount),避免linkCount=0时出错
|
|
|
|
|
const baseSize = 15; // 基础大小(连接数为0时的大小)
|
|
|
|
|
const logGrowth = 8 * Math.log1p(linkCount); // 对数增长因子
|
|
|
|
|
const maxSize = 40; // 节点最大尺寸上限
|
|
|
|
|
// 最终大小 = 基础大小 + 对数增长,且不超过最大值
|
|
|
|
|
const symbolSize = Math.min(baseSize + logGrowth, maxSize);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
id: node.id,
|
|
|
|
|
name: node.labels?.[0] || '未知节点',
|
|
|
|
|
category: node.properties?.entity_type || '默认类型',
|
|
|
|
|
properties: node.properties || {},
|
|
|
|
|
symbolSize: symbolSize
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const echartsLinks = kgData.edges.map(edge => ({
|
|
|
|
|
source: edge.source,
|
|
|
|
|
@ -2073,7 +2082,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
.map(type => ({ name: type }));
|
|
|
|
|
|
|
|
|
|
// 初始化ECharts并渲染
|
|
|
|
|
const chart = echarts.init(document.getElementById('graph-container'));
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const graphContainer = document.getElementById('graph-container');
|
|
|
|
|
// 计算并设置图谱容器高度(减去节点信息区域高度)
|
|
|
|
|
const nodeInfoHeight = document.getElementById('node-info').offsetHeight;
|
|
|
|
|
graphContainer.style.height = (graphContainer.parentElement.clientHeight - nodeInfoHeight) + 'px';
|
|
|
|
|
|
|
|
|
|
const chart = echarts.init(graphContainer);
|
|
|
|
|
|
|
|
|
|
chart.setOption({
|
|
|
|
|
tooltip: {
|
|
|
|
|
@ -2092,10 +2107,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
roam: true,
|
|
|
|
|
draggable: true,
|
|
|
|
|
force: {
|
|
|
|
|
repulsion: 300,
|
|
|
|
|
edgeLength: 100,
|
|
|
|
|
repulsion: 400,
|
|
|
|
|
edgeLength: 150,
|
|
|
|
|
gravity: 0.1,
|
|
|
|
|
iterations: 50
|
|
|
|
|
iterations: 100
|
|
|
|
|
},
|
|
|
|
|
label: {
|
|
|
|
|
show: true,
|
|
|
|
|
@ -2126,7 +2141,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
nodeInfo.innerHTML = `
|
|
|
|
|
<strong>${params.name}</strong> (${params.data.category})<br>
|
|
|
|
|
连接数: ${nodeLinkCount[params.data.id]}<br>
|
|
|
|
|
${props.description ? `描述: ${props.description.substring(0, 150)}${props.description.length > 150 ? '...' : ''}<br>` : ''}
|
|
|
|
|
${props.description ? `描述: ${props.description.substring(0, 800)}${props.description.length > 800 ? '...' : ''}<br>` : ''}
|
|
|
|
|
${props.source ? `来源: ${props.source}` : ''}
|
|
|
|
|
`;
|
|
|
|
|
} else if (params.dataType === 'edge') {
|
|
|
|
|
@ -2141,7 +2156,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 窗口大小变化时重绘
|
|
|
|
|
window.addEventListener('resize', () => chart.resize());
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
|
const nodeInfoHeight = document.getElementById('node-info').offsetHeight;
|
|
|
|
|
graphContainer.style.height = (graphContainer.parentElement.clientHeight - nodeInfoHeight) + 'px';
|
|
|
|
|
chart.resize();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
|
|
// 更新状态信息
|
|
|
|
|
nodeInfo.className = '';
|
|
|
|
|
|