ForcePilot/web/src/components/KnowledgeGraphSection.vue
Wenjie Zhang 7a23ce4742 refactor: 优化界面组件和样式,简化代码结构
- 移除知识图谱查看器的图例和部分控制按钮
- 调整数据库信息视图的面板宽度和背景色
- 简化知识库工厂注册参数
- 移除文件表格的自动刷新和面板切换按钮
- 优化代理配置侧边栏的系统提示词编辑体验
2025-09-06 14:04:33 +08:00

330 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="graph-section" :class="{ collapsed: !visible }" :style="style" v-if="isGraphSupported">
<div class="section-header">
<div class="header-left">
<h3 class="section-title">知识图谱</h3>
<div v-if="graphStats.displayed_nodes > 0 || graphStats.displayed_edges > 0" class="graph-stats">
<a-tag color="blue" size="small">节点: {{ graphStats.displayed_nodes }}</a-tag>
<a-tag color="green" size="small">边: {{ graphStats.displayed_edges }}</a-tag>
<!-- <a-tag v-if="graphStats.is_truncated" color="red" size="small">已截断</a-tag> -->
</div>
</div>
<div class="panel-actions">
<a-button
type="primary"
size="small"
@click="loadGraph"
:disabled="!isGraphSupported"
:icon='h(ReloadOutlined)'
>
加载图谱
</a-button>
<!-- 导出功能暂禁,等待 LightRAG 库修复
<a-button
type="primary"
size="small"
@click="showExportModal = true"
:disabled="!isGraphSupported"
>
导出图谱
</a-button>
-->
<!-- <a-button
type="text"
size="small"
@click="showSettings = true"
>
<SettingOutlined />
参数
</a-button> -->
<!-- <a-button
type="text"
size="small"
:icon="h(DeleteOutlined)"
title="清空"
@click="clearGraph"
:disabled="!isGraphSupported"
>
清空
</a-button> -->
<a-button
type="text"
size="small"
:icon="h(ExpandOutlined)"
title="最大化"
@click="toggleGraphMaximize"
:disabled="!isGraphSupported"
>
最大化
</a-button>
<a-button
type="text"
size="small"
@click="toggleVisible"
title="折叠/展开"
>
<component :is="visible ? UpOutlined : DownOutlined" />
</a-button>
</div>
</div>
<div class="graph-container-compact content" v-show="visible">
<div v-if="!isGraphSupported" class="graph-disabled">
<div class="disabled-content">
<h4>知识图谱不可用</h4>
<p>当前知识库类型 "{{ kbTypeLabel }}" 不支持知识图谱功能。</p>
<p>只有 LightRAG 类型的知识库支持知识图谱。</p>
</div>
</div>
<KnowledgeGraphViewer
v-else
:initial-database-id="databaseId"
:hide-db-selector="true"
:hide-stats="true"
:hide-controls="!store.state.isGraphMaximized"
:initial-limit="graphLimit"
:initial-depth="graphDepth"
@update:stats="handleStatsUpdate"
ref="graphViewerRef"
/>
</div>
<!-- 设置模态框 -->
<a-modal
v-model:open="showSettings"
title="图谱设置"
:footer="null"
width="300px"
>
<div class="settings-form">
<a-form layout="vertical">
<a-form-item label="最大节点数 (limit)">
<a-input-number
v-model:value="graphLimit"
:min="10"
:max="1000"
:step="10"
style="width: 100%"
/>
</a-form-item>
<a-form-item label="搜索深度 (depth)">
<a-input-number
v-model:value="graphDepth"
:min="1"
:max="5"
:step="1"
style="width: 100%"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="applySettings" style="width: 100%">
应用
</a-button>
</a-form-item>
</a-form>
</div>
</a-modal>
<!-- 导出功能暂禁,等待 LightRAG 库修复
<a-modal
v-model:open="showExportModal"
title="导出图谱数据"
@ok="handleExport"
ok-text="导出"
cancel-text="取消"
>
<a-form layout="vertical">
<a-form-item label="导出格式">
<a-select v-model:value="exportOptions.format">
<a-select-option value="zip">ZIP 压缩包</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-checkbox v-model:checked="exportOptions.include_vectors" disabled>
包含向量数据 (暂不支持)
</a-checkbox>
</a-form-item>
</a-form>
</a-modal>
-->
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useDatabaseStore } from '@/stores/database';
import { useUserStore } from '@/stores/user';
import { ReloadOutlined, DeleteOutlined, ExpandOutlined, UpOutlined, DownOutlined, SettingOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import KnowledgeGraphViewer from '@/components/KnowledgeGraphViewer.vue';
import { h } from 'vue';
const store = useDatabaseStore();
const userStore = useUserStore();
const props = defineProps({
visible: {
type: Boolean,
default: true
},
style: {
type: Object,
default: () => ({})
},
});
// 添加调试日志
console.log('KnowledgeGraphSection props:', props);
console.log('KnowledgeGraphSection style prop:', props.style);
const emit = defineEmits(['toggleVisible']);
const databaseId = computed(() => store.databaseId);
const kbType = computed(() => store.database.kb_type);
const graphStats = computed({
get: () => store.graphStats,
set: (stats) => store.graphStats = stats
});
const graphViewerRef = ref(null);
const showSettings = ref(false);
const graphLimit = ref(200);
const graphDepth = ref(2);
const showExportModal = ref(false);
const exportOptions = ref({
format: 'zip',
include_vectors: false,
});
// 计算属性:是否支持知识图谱
const isGraphSupported = computed(() => {
const type = kbType.value?.toLowerCase();
return type === 'lightrag';
});
const toggleVisible = () => {
emit('toggleVisible');
};
const loadGraph = () => {
if (!(Object.keys(store.database?.files).length > 0)) {
return;
}
if (graphViewerRef.value && typeof graphViewerRef.value.loadFullGraph === 'function') {
graphViewerRef.value.loadFullGraph();
}
};
const clearGraph = () => {
if (graphViewerRef.value && typeof graphViewerRef.value.clearGraph === 'function') {
graphViewerRef.value.clearGraph();
}
};
const toggleGraphMaximize = () => {
store.state.isGraphMaximized = !store.state.isGraphMaximized;
};
const handleStatsUpdate = (stats) => {
graphStats.value = stats;
};
const applySettings = () => {
showSettings.value = false;
// 设置已通过props传递给子组件不需要额外操作
};
// const handleExport = async () => {
// const dbId = store.databaseId;
// if (!dbId) {
// message.error('请先选择一个知识库');
// return;
// }
// try {
// const response = await fetch(`/api/knowledge/databases/${dbId}/export?format=${exportOptions.value.format}`, {
// headers: {
// ...userStore.getAuthHeaders()
// }
// });
// if (!response.ok) {
// const errorData = await response.json();
// throw new Error(errorData.detail || `导出失败: ${response.statusText}`);
// }
// const blob = await response.blob();
// const url = window.URL.createObjectURL(blob);
// const a = document.createElement('a');
// a.style.display = 'none';
// a.href = url;
// const disposition = response.headers.get('content-disposition');
// let filename = `export_${dbId}.zip`;
// if (disposition) {
// const filenameMatch = disposition.match(/filename="([^"]+)"/);
// if (filenameMatch && filenameMatch[1]) {
// filename = filenameMatch[1];
// }
// }
// a.download = filename;
// document.body.appendChild(a);
// a.click();
// window.URL.revokeObjectURL(url);
// document.body.removeChild(a);
// message.success('导出任务已开始');
// showExportModal.value = false;
// } catch (error) {
// console.error('导出图谱失败:', error);
// message.error(error.message || '导出图谱失败');
// }
// };
watch(isGraphSupported, (supported) => {
if (supported) {
setTimeout(() => {
loadGraph();
}, 800);
}
});
</script>
<style scoped lang="less">
.graph-section {
.graph-container-compact {
flex: 1;
min-height: 0;
padding: 0;
height: 100%;
border-radius: 0;
}
.graph-disabled {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.disabled-content {
text-align: center;
color: #8c8c8c;
h4 {
margin-bottom: 8px;
}
}
.content {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
height: 100%;
}
}
</style>