马一丁

Beautify the front-end page display of GraphRAG

... ... @@ -1486,47 +1486,50 @@
color: #666;
font-size: 14px;
}
/* 知识图谱迷你面板 */
.graph-mini-panel {
/* 知识图谱面板(与左侧内容同宽) */
.graph-panel {
width: 100%;
border: 2px solid #000000;
background-color: #f7f7f7;
padding: 10px 12px;
width: 280px;
align-self: flex-start;
background-color: #ffffff;
display: none;
box-shadow: 4px 4px 0 #0000001a;
flex-direction: column;
gap: 8px;
}
.graph-mini-panel.collapsed .graph-mini-body {
.graph-panel.collapsed .graph-panel-body {
display: none;
}
.graph-mini-header {
.graph-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
align-items: center;
padding: 12px 14px;
border-bottom: 2px solid #000000;
background-color: #f8f9fa;
gap: 10px;
}
.graph-mini-title {
.graph-panel-title {
font-weight: bold;
font-size: 14px;
font-size: 15px;
}
.graph-mini-subtitle {
.graph-panel-subtitle {
font-size: 12px;
color: #555555;
line-height: 1.2;
color: #555;
margin-top: 4px;
line-height: 1.3;
}
.graph-mini-actions {
.graph-panel-actions {
display: flex;
gap: 6px;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.graph-mini-button {
.graph-panel-button {
border: 1px solid #000000;
background-color: #ffffff;
padding: 6px 10px;
... ... @@ -1536,40 +1539,143 @@
transition: background-color 0.2s ease, color 0.2s ease;
}
.graph-mini-button:hover {
.graph-panel-button:hover {
background-color: #000000;
color: #ffffff;
}
.graph-mini-body {
.graph-status-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border: 1px solid #000000;
font-size: 12px;
font-weight: bold;
}
.graph-status-chip.idle { background-color: #fff7ed; color: #b45309; }
.graph-status-chip.loading { background-color: #fef3c7; color: #92400e; }
.graph-status-chip.ready { background-color: #e8f5e9; color: #2f855a; }
.graph-status-chip.error { background-color: #fef2f2; color: #b91c1c; }
.graph-panel-body {
padding: 10px 12px 14px 12px;
display: flex;
flex-direction: column;
gap: 10px;
}
.graph-panel-toolbar {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
justify-content: space-between;
}
.graph-toolbar-left {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.graph-stats {
display: flex;
gap: 10px;
font-size: 12px;
color: #333;
}
.graph-stats .stat-label {
font-weight: bold;
margin-right: 4px;
}
.graph-filter-group {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.graph-filter-item {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
border: 1px solid #000000;
background-color: #ffffff;
cursor: pointer;
font-size: 12px;
user-select: none;
}
.graph-filter-item input {
accent-color: #000000;
}
.graph-filter-count {
color: #666666;
font-size: 11px;
}
.graph-search {
display: flex;
gap: 8px;
align-items: center;
}
.graph-search input {
padding: 8px 10px;
border: 1px solid #000000;
min-width: 200px;
}
.graph-panel-canvas {
position: relative;
height: 240px;
width: 240px;
height: 360px;
width: 100%;
border: 1px dashed #000000;
background-color: #ffffff;
overflow: hidden;
}
.graph-mini-placeholder {
.graph-panel-placeholder {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
text-align: center;
padding: 10px;
font-size: 13px;
color: #555;
background-color: #ffffff;
z-index: 2;
}
.graph-panel-placeholder.error {
color: #b42318;
}
.graph-panel-detail {
border: 1px solid #e5e5e5;
padding: 8px;
background-color: #fafafa;
font-size: 12px;
color: #666666;
line-height: 1.5;
}
.graph-mini-placeholder.error {
color: #b42318;
.graph-panel-detail .detail-title {
font-weight: bold;
margin-bottom: 4px;
}
.graph-mini-canvas {
height: 100%;
width: 100%;
display: none;
.graph-panel-detail .detail-meta {
color: #555;
margin-bottom: 6px;
}
</style>
... ... @@ -1736,9 +1842,12 @@
let configDirty = false;
let graphragEnabled = false;
let graphragSettingLoaded = false;
let graphMiniNetwork = null;
let graphMiniPreferredTaskId = null;
let graphMiniLoading = false;
let graphPanelNetwork = null;
let graphPanelData = { nodes: [], edges: [] };
let graphPanelFilters = new Set(['topic', 'engine', 'section', 'search_query', 'source']);
let graphPanelTaskId = null;
let graphPanelState = 'idle';
let graphPanelLoading = false;
let configAutoRefreshTimer = null;
let systemStarted = false;
let systemStarting = false;
... ... @@ -4882,21 +4991,58 @@ function getConsoleContainer() {
<!-- 任务进度区域 -->
<div id="taskProgressArea"></div>
<!-- 知识图谱迷你预览(GraphRAG 开启时显示) -->
<div class="graph-mini-panel" id="graphMiniPanel">
<div class="graph-mini-header">
<!-- 知识图谱面板(GraphRAG 开启时显示) -->
<div class="graph-panel" id="graphPanel">
<div class="graph-panel-header">
<div>
<div class="graph-mini-title">知识图谱</div>
<div class="graph-mini-subtitle">GraphRAG 生成概览</div>
<div class="graph-panel-title">知识图谱</div>
<div class="graph-panel-subtitle">GraphRAG 节点关系可视化</div>
</div>
<div class="graph-mini-actions">
<button class="graph-mini-button" id="graphMiniRefresh" title="刷新知识图谱">刷新</button>
<button class="graph-mini-button" id="graphMiniToggle" title="折叠/展开">折叠</button>
<div class="graph-panel-actions">
<span class="graph-status-chip idle" id="graphStatusChip">未生成</span>
<button class="graph-panel-button" id="graphFullBtn" title="在新标签页查看">全屏</button>
<button class="graph-panel-button" id="graphRefreshBtn" title="刷新知识图谱">刷新</button>
<button class="graph-panel-button" id="graphCollapseBtn" title="折叠/展开">收起</button>
</div>
</div>
<div class="graph-mini-body" id="graphMiniBody">
<div class="graph-mini-placeholder" id="graphMiniPlaceholder">等待图谱生成...</div>
<div class="graph-mini-canvas" id="graphMiniCanvas"></div>
<div class="graph-panel-body" id="graphPanelBody">
<div class="graph-panel-toolbar">
<div class="graph-toolbar-left">
<div class="graph-stats">
<span><span class="stat-label">节点</span><span id="graphNodeCount">0</span></span>
<span><span class="stat-label">关系</span><span id="graphEdgeCount">0</span></span>
</div>
<div class="graph-filter-group" id="graphFilterGroup">
<label class="graph-filter-item">
<input type="checkbox" data-type="topic" checked> 主题 <span class="graph-filter-count" data-type-count="topic">(0)</span>
</label>
<label class="graph-filter-item">
<input type="checkbox" data-type="engine" checked> 引擎 <span class="graph-filter-count" data-type-count="engine">(0)</span>
</label>
<label class="graph-filter-item">
<input type="checkbox" data-type="section" checked> 报告段落 <span class="graph-filter-count" data-type-count="section">(0)</span>
</label>
<label class="graph-filter-item">
<input type="checkbox" data-type="search_query" checked> 搜索词 <span class="graph-filter-count" data-type-count="search_query">(0)</span>
</label>
<label class="graph-filter-item">
<input type="checkbox" data-type="source" checked> 数据来源 <span class="graph-filter-count" data-type-count="source">(0)</span>
</label>
</div>
</div>
<div class="graph-search">
<input type="text" id="graphSearchInput" placeholder="搜索节点...">
<button class="graph-panel-button" id="graphFitBtn">适应</button>
</div>
</div>
<div class="graph-panel-canvas" id="graphPanelCanvas">
<div class="graph-panel-placeholder" id="graphPanelPlaceholder">等待图谱生成...</div>
</div>
<div class="graph-panel-detail" id="graphPanelDetail" style="display: none;">
<div class="detail-title" id="graphDetailTitle"></div>
<div class="detail-meta" id="graphDetailMeta"></div>
<div class="detail-props" id="graphDetailProps"></div>
</div>
</div>
</div>
... ... @@ -4910,7 +5056,7 @@ function getConsoleContainer() {
reportContent.innerHTML = interfaceHTML;
initializeReportControls();
initializeGraphMiniPanel(statusData);
initializeGraphPanel(statusData);
resetReportStreamOutput('等待新的Report任务启动...');
updateReportStreamStatus('idle');
... ... @@ -4942,8 +5088,8 @@ function getConsoleContainer() {
}
}
async function initializeGraphMiniPanel(statusData) {
const panel = document.getElementById('graphMiniPanel');
async function initializeGraphPanel(statusData) {
const panel = document.getElementById('graphPanel');
if (!panel) return;
const enabled = await ensureGraphragSetting();
... ... @@ -4952,115 +5098,223 @@ function getConsoleContainer() {
return;
}
panel.style.display = 'block';
bindGraphMiniEvents();
panel.style.display = 'flex';
bindGraphPanelEvents();
const currentTaskId = statusData && statusData.current_task
? statusData.current_task.task_id
: (lastCompletedReportTask ? lastCompletedReportTask.task_id : null);
const currentTaskStatus = statusData && statusData.current_task
? statusData.current_task.status
: '';
if (currentTaskId) {
graphMiniPreferredTaskId = currentTaskId;
}
const currentTask = statusData && statusData.current_task ? statusData.current_task : null;
graphPanelTaskId = currentTask?.task_id || (lastCompletedReportTask ? lastCompletedReportTask.task_id : null);
if (currentTaskStatus === 'running') {
setGraphMiniWaiting(currentTaskId);
if (currentTask && currentTask.status === 'running') {
setGraphPanelAwaiting(graphPanelTaskId);
return;
}
if (panel.classList.contains('collapsed')) {
setGraphMiniPlaceholder('展开以查看知识图谱');
return;
refreshGraphPanel(graphPanelTaskId, true);
}
function bindGraphPanelEvents() {
const refreshBtn = document.getElementById('graphRefreshBtn');
const collapseBtn = document.getElementById('graphCollapseBtn');
const fullBtn = document.getElementById('graphFullBtn');
const fitBtn = document.getElementById('graphFitBtn');
const searchInput = document.getElementById('graphSearchInput');
const filterGroup = document.getElementById('graphFilterGroup');
if (refreshBtn && !refreshBtn.dataset.bound) {
refreshBtn.dataset.bound = 'true';
refreshBtn.addEventListener('click', () => refreshGraphPanel(graphPanelTaskId, true));
}
if (collapseBtn && !collapseBtn.dataset.bound) {
collapseBtn.dataset.bound = 'true';
collapseBtn.addEventListener('click', () => {
const panel = document.getElementById('graphPanel');
if (!panel) return;
const collapsed = panel.classList.toggle('collapsed');
collapseBtn.textContent = collapsed ? '展开' : '收起';
if (!collapsed) {
refreshGraphPanel(graphPanelTaskId, false);
} else {
setGraphPanelPlaceholder('已折叠,展开后可查看知识图谱');
}
});
}
refreshGraphMini(graphMiniPreferredTaskId, true);
}
if (fullBtn && !fullBtn.dataset.bound) {
fullBtn.dataset.bound = 'true';
fullBtn.addEventListener('click', () => {
const target = graphPanelTaskId || (lastCompletedReportTask ? lastCompletedReportTask.task_id : null);
const url = target ? `/graph-viewer/${target}` : '/graph-viewer';
window.open(url, '_blank');
});
}
function bindGraphMiniEvents() {
const toggleBtn = document.getElementById('graphMiniToggle');
const refreshBtn = document.getElementById('graphMiniRefresh');
if (fitBtn && !fitBtn.dataset.bound) {
fitBtn.dataset.bound = 'true';
fitBtn.addEventListener('click', () => {
if (graphPanelNetwork) {
graphPanelNetwork.fit({ animation: { duration: 300, easing: 'easeInOutQuad' } });
}
});
}
if (toggleBtn && !toggleBtn.dataset.bound) {
toggleBtn.dataset.bound = 'true';
toggleBtn.addEventListener('click', toggleGraphMiniPanel);
if (searchInput && !searchInput.dataset.bound) {
searchInput.dataset.bound = 'true';
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
focusGraphByKeyword(searchInput.value);
}
});
searchInput.addEventListener('input', () => {
if (!searchInput.value) {
clearGraphSelection();
}
});
}
if (refreshBtn && !refreshBtn.dataset.bound) {
refreshBtn.dataset.bound = 'true';
refreshBtn.addEventListener('click', () => refreshGraphMini(graphMiniPreferredTaskId, true));
if (filterGroup && !filterGroup.dataset.bound) {
filterGroup.dataset.bound = 'true';
filterGroup.addEventListener('change', (event) => {
const checkbox = event.target.closest('input[type="checkbox"][data-type]');
if (!checkbox) return;
const type = (checkbox.dataset.type || '').toLowerCase();
if (checkbox.checked) {
graphPanelFilters.add(type);
} else {
graphPanelFilters.delete(type);
}
renderGraphPanel(graphPanelData, false);
});
}
}
function toggleGraphMiniPanel() {
const panel = document.getElementById('graphMiniPanel');
const toggleBtn = document.getElementById('graphMiniToggle');
if (!panel) return;
const collapsed = panel.classList.toggle('collapsed');
if (toggleBtn) {
toggleBtn.textContent = collapsed ? '展开' : '折叠';
}
if (!collapsed) {
refreshGraphMini(graphMiniPreferredTaskId, true);
} else {
setGraphMiniPlaceholder('已折叠,展开以查看知识图谱');
function setGraphPanelState(state, message = '') {
graphPanelState = state;
const chip = document.getElementById('graphStatusChip');
if (!chip) return;
chip.classList.remove('idle', 'loading', 'ready', 'error');
chip.classList.add(state);
const textMap = {
idle: '未生成',
loading: '正在生成',
ready: '已生成',
error: '加载失败'
};
chip.textContent = textMap[state] || state;
if (message) {
setGraphPanelPlaceholder(message, state === 'error' ? 'error' : '');
}
}
function setGraphMiniPlaceholder(message, type = '') {
const placeholder = document.getElementById('graphMiniPlaceholder');
const canvas = document.getElementById('graphMiniCanvas');
if (!placeholder || !canvas) return;
function setGraphPanelPlaceholder(message, type = '') {
const placeholder = document.getElementById('graphPanelPlaceholder');
if (!placeholder) return;
placeholder.textContent = message || '';
if (type === 'error') {
placeholder.classList.add('error');
} else {
placeholder.classList.remove('error');
}
placeholder.style.display = 'flex';
canvas.style.display = 'none';
placeholder.classList.toggle('error', type === 'error');
placeholder.style.display = message ? 'flex' : 'none';
}
async function fetchGraphData(taskId = null) {
const url = taskId ? `/api/graph/${taskId}` : '/api/graph/latest';
try {
const response = await fetch(url, { cache: 'no-store' });
const data = await response.json();
if (!response.ok || !data.success || !data.graph) {
return null;
function updateGraphStats(allData, filteredNodes = null, filteredEdges = null) {
const nodeCountEl = document.getElementById('graphNodeCount');
const edgeCountEl = document.getElementById('graphEdgeCount');
if (nodeCountEl) nodeCountEl.textContent = (filteredNodes || allData.nodes || []).length;
if (edgeCountEl) edgeCountEl.textContent = (filteredEdges || allData.edges || []).length;
const typeCounts = {};
(allData.nodes || []).forEach(node => {
const key = (node.group || node.type || 'other').toLowerCase();
typeCounts[key] = (typeCounts[key] || 0) + 1;
});
document.querySelectorAll('.graph-filter-count').forEach(el => {
const t = el.dataset.typeCount;
el.textContent = `(${typeCounts[t] || 0})`;
});
}
function renderGraphPanel(graph, resetPlaceholder = true) {
const panel = document.getElementById('graphPanel');
const canvasWrapper = document.getElementById('graphPanelCanvas');
if (!panel || !canvasWrapper) return;
graphPanelData = graph || { nodes: [], edges: [] };
const allowed = graphPanelFilters;
const nodes = (graphPanelData.nodes || []).filter(node => allowed.has((node.group || node.type || '').toLowerCase()));
const nodeIdSet = new Set(nodes.map(n => n.id));
const edges = (graphPanelData.edges || []).filter(edge => nodeIdSet.has(edge.from) && nodeIdSet.has(edge.to));
updateGraphStats(graphPanelData, nodes, edges);
const placeholder = document.getElementById('graphPanelPlaceholder');
if (!nodes.length) {
setGraphPanelState('ready', '未找到匹配的节点,请调整筛选或稍后再试');
if (placeholder) placeholder.style.display = 'flex';
if (graphPanelNetwork) {
graphPanelNetwork.destroy();
graphPanelNetwork = null;
}
return data;
} catch (error) {
console.warn('获取知识图谱失败:', error);
return null;
return;
}
if (resetPlaceholder && placeholder) {
placeholder.style.display = 'none';
}
}
function renderGraphMini(graph) {
const canvas = document.getElementById('graphMiniCanvas');
const placeholder = document.getElementById('graphMiniPlaceholder');
if (!canvas || !placeholder) return;
if (!(window.vis && window.vis.Network)) {
setGraphMiniPlaceholder('图谱组件未加载,请检查网络后重试', 'error');
updateGraphStats({ nodes: [], edges: [] });
setGraphPanelState('error', '图谱组件未加载,请检查网络后重试');
return;
}
const nodes = new vis.DataSet((graph.nodes || []).map(node => ({
id: node.id,
label: node.label || node.id,
group: node.group || node.type,
title: node.title || '',
size: 12
})));
const container = canvasWrapper;
let canvas = document.getElementById('graphPanelCanvasInner');
if (!canvas) {
canvas = document.createElement('div');
canvas.id = 'graphPanelCanvasInner';
canvas.style.height = '100%';
canvas.style.width = '100%';
container.appendChild(canvas);
} else {
canvas.innerHTML = '';
}
setGraphPanelPlaceholder('');
const edges = new vis.DataSet((graph.edges || []).map(edge => ({
const colorMap = {
topic: '#ef4444',
engine: '#f59e0b',
section: '#10b981',
search_query: '#3b82f6',
source: '#8b5cf6'
};
const nodeData = new vis.DataSet(nodes.map(node => {
const nodeType = (node.group || node.type || '').toLowerCase();
return {
id: node.id,
label: node.label || node.id,
group: node.group || node.type,
title: node.title || '',
properties: node.properties || {},
size: 14,
color: {
background: colorMap[nodeType] || '#ffffff',
border: '#000000',
highlight: {
background: colorMap[nodeType] || '#e0e0e0',
border: '#000000'
}
}
};
}));
const edgeData = new vis.DataSet(edges.map(edge => ({
from: edge.from,
to: edge.to,
label: edge.label || '',
arrows: 'to',
color: '#444444',
font: { align: 'top', size: 10 }
font: { align: 'top', size: 10 },
color: '#555555',
smooth: true
})));
const options = {
... ... @@ -5071,83 +5325,154 @@ function getConsoleContainer() {
shape: 'dot',
borderWidth: 1,
font: { size: 12 },
scaling: { min: 8, max: 18 }
scaling: { min: 8, max: 20 }
},
edges: {
smooth: true,
color: '#999999',
width: 1
},
physics: {
stabilization: true,
barnesHut: { avoidOverlap: 0.5, springLength: 80, springConstant: 0.02 }
barnesHut: { avoidOverlap: 0.3, springLength: 90, springConstant: 0.02 }
}
};
if (graphMiniNetwork) {
graphMiniNetwork.destroy();
if (graphPanelNetwork) {
graphPanelNetwork.destroy();
}
graphPanelNetwork = new vis.Network(canvas, { nodes: nodeData, edges: edgeData }, options);
placeholder.style.display = 'none';
canvas.style.display = 'block';
graphMiniNetwork = new vis.Network(canvas, { nodes, edges }, options);
const fitOnce = () => {
graphMiniNetwork.fit({ animation: { duration: 300, easing: 'easeInOutQuad' } });
graphMiniNetwork.off('stabilizationIterationsDone', fitOnce);
graphPanelNetwork.fit({ animation: { duration: 300, easing: 'easeInOutQuad' } });
graphPanelNetwork.off('stabilizationIterationsDone', fitOnce);
};
graphMiniNetwork.on('stabilizationIterationsDone', fitOnce);
graphPanelNetwork.on('stabilizationIterationsDone', fitOnce);
graphPanelNetwork.on('selectNode', params => {
const selectedId = params.nodes[0];
const selectedNode = nodes.find(n => n.id === selectedId);
showGraphDetail(selectedNode);
});
graphPanelNetwork.on('deselectNode', hideGraphDetail);
}
function showGraphDetail(node) {
const detail = document.getElementById('graphPanelDetail');
const title = document.getElementById('graphDetailTitle');
const meta = document.getElementById('graphDetailMeta');
const props = document.getElementById('graphDetailProps');
if (!detail || !title || !meta || !props) return;
if (!node) {
hideGraphDetail();
return;
}
detail.style.display = 'block';
title.textContent = node.label || node.id;
meta.textContent = `类型: ${(node.group || node.type || '未知')}`;
const properties = node.properties || {};
const lines = Object.entries(properties).map(([k, v]) => `<div><strong>${k}:</strong> ${v}</div>`);
props.innerHTML = lines.length ? lines.join('') : '暂无更多属性';
}
function hideGraphDetail() {
const detail = document.getElementById('graphPanelDetail');
if (detail) {
detail.style.display = 'none';
}
}
async function refreshGraphMini(taskId = null, allowFallback = true) {
const panel = document.getElementById('graphMiniPanel');
function focusGraphByKeyword(keyword) {
if (!graphPanelNetwork || !keyword) return;
const lower = keyword.toLowerCase();
const nodes = graphPanelNetwork.body.data.nodes.get();
const matched = nodes.filter(n => (n.label || '').toLowerCase().includes(lower));
if (matched.length) {
const ids = matched.map(n => n.id);
graphPanelNetwork.selectNodes(ids);
graphPanelNetwork.focus(ids[0], { scale: 1, animation: { duration: 300, easing: 'easeInOutQuad' } });
showGraphDetail(matched[0]);
}
}
function clearGraphSelection() {
if (graphPanelNetwork) {
graphPanelNetwork.unselectAll();
}
hideGraphDetail();
}
async function fetchGraphData(taskId = null) {
const url = taskId ? `/api/graph/${taskId}` : '/api/graph/latest';
try {
const response = await fetch(url, { cache: 'no-store' });
const data = await response.json();
if (!response.ok || !data.success || !data.graph) {
return null;
}
return data;
} catch (error) {
console.warn('获取知识图谱失败:', error);
return null;
}
}
async function refreshGraphPanel(taskId = null, allowFallback = true) {
const panel = document.getElementById('graphPanel');
if (!panel) return;
const enabled = await ensureGraphragSetting();
if (!enabled) {
panel.style.display = 'none';
return;
}
panel.style.display = 'flex';
bindGraphPanelEvents();
if (panel.classList.contains('collapsed')) {
return;
}
bindGraphMiniEvents();
panel.style.display = 'block';
if (taskId) {
graphMiniPreferredTaskId = taskId;
graphPanelTaskId = taskId;
}
if (graphMiniLoading) {
return;
}
if (graphPanelLoading) return;
graphPanelLoading = true;
setGraphPanelState('loading', '正在加载知识图谱...');
const targetTaskId = taskId || graphMiniPreferredTaskId || (lastCompletedReportTask ? lastCompletedReportTask.task_id : null);
setGraphMiniPlaceholder('加载知识图谱...');
graphMiniLoading = true;
try {
const targetTaskId = graphPanelTaskId || (lastCompletedReportTask ? lastCompletedReportTask.task_id : null);
let data = await fetchGraphData(targetTaskId);
if (!data && allowFallback && targetTaskId) {
data = await fetchGraphData(null);
}
if (data && data.graph) {
renderGraphMini(data.graph);
graphPanelTaskId = targetTaskId || data.report_id || graphPanelTaskId;
renderGraphPanel(data.graph);
setGraphPanelState('ready');
} else {
setGraphMiniPlaceholder('暂未找到知识图谱,请稍后点击刷新重试', 'error');
updateGraphStats({ nodes: [], edges: [] });
setGraphPanelState('idle', '暂未找到知识图谱,请生成报告后刷新');
}
} catch (error) {
console.warn('刷新知识图谱失败:', error);
updateGraphStats({ nodes: [], edges: [] });
setGraphPanelState('error', '加载知识图谱失败,请稍后重试');
} finally {
graphMiniLoading = false;
graphPanelLoading = false;
}
}
function setGraphMiniWaiting(taskId) {
graphMiniPreferredTaskId = taskId || graphMiniPreferredTaskId;
function setGraphPanelAwaiting(taskId) {
graphPanelTaskId = taskId || graphPanelTaskId;
ensureGraphragSetting().then(enabled => {
if (!enabled) return;
const panel = document.getElementById('graphMiniPanel');
const panel = document.getElementById('graphPanel');
if (!panel) return;
panel.style.display = 'block';
bindGraphMiniEvents();
setGraphMiniPlaceholder('等待图谱生成...');
panel.style.display = 'flex';
bindGraphPanelEvents();
updateGraphStats({ nodes: [], edges: [] });
setGraphPanelState('loading', '报告生成中,知识图谱生成后自动刷新');
setGraphPanelPlaceholder('正在生成知识图谱...');
});
}
... ... @@ -5548,7 +5873,7 @@ function getConsoleContainer() {
if (data.success) {
reportTaskId = data.task_id;
showMessage('报告生成已启动', 'success');
setGraphMiniWaiting(reportTaskId);
setGraphPanelAwaiting(reportTaskId);
// 更新任务状态显示
updateTaskProgressStatus({
... ... @@ -5638,8 +5963,8 @@ function getConsoleContainer() {
if (data.task.status === 'completed') {
stopProgressPolling();
showMessage('报告生成完成!', 'success');
graphMiniPreferredTaskId = data.task.task_id;
refreshGraphMini(data.task.task_id, true);
graphPanelTaskId = data.task.task_id;
refreshGraphPanel(data.task.task_id, true);
// 自动显示报告
viewReport(taskId);
... ... @@ -5929,13 +6254,13 @@ function getConsoleContainer() {
if (eventType === 'status' && task) {
if (task.status === 'running') {
resetReportLogsForNewTask(task.task_id, '收到流式状态事件,已重置日志');
setGraphMiniWaiting(task.task_id);
setGraphPanelAwaiting(task.task_id);
}
updateTaskProgressStatus(task);
reportTaskId = task.status === 'running' ? task.task_id : null;
if (task.status === 'completed') {
lastCompletedReportTask = task;
graphMiniPreferredTaskId = task.task_id;
graphPanelTaskId = task.task_id;
setGenerateButtonState(false);
} else if (task.status === 'running') {
setGenerateButtonState(true);
... ... @@ -6027,8 +6352,8 @@ function getConsoleContainer() {
if (task) {
lastCompletedReportTask = task;
updateDownloadButtonState(task);
graphMiniPreferredTaskId = task.task_id;
refreshGraphMini(task.task_id, true);
graphPanelTaskId = task.task_id;
refreshGraphPanel(task.task_id, true);
}
if (eventData.task_id && !reportAutoPreviewLoaded) {
viewReport(eventData.task_id);
... ...