From 32cc88814a373054d98354eed4dcee92ad94034c Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Thu, 25 Sep 2025 04:27:24 +0800 Subject: [PATCH 01/11] modified README --- README.md | 38 +++++++++++++++++++++----- code/backend/README.md | 28 ------------------- code/frontend/README.md | 59 ----------------------------------------- 3 files changed, 31 insertions(+), 94 deletions(-) delete mode 100644 code/backend/README.md delete mode 100644 code/frontend/README.md diff --git a/README.md b/README.md index 576e3f4..78b3285 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,37 @@ -➜ 3022202376 tree +# 知识图谱后端服务 -. +## 环境要求 + jdk 11 + nodejs 22.19.0 + angular_cli 20.3.2 + neo4j 4.4 + express 4.18.2 + AntV_G6 4.3.3 -├── code code of project +## 安装依赖 +在 ./code/backend 目录运行 -├── data data set + npm install + npm install express@4.18.2 + npm install --save @antv/g6@4.3.3 -├── member_info.txt information of group members +## 加载数据 +1. 确保neo4j处于停机状态 +2. 在neo4j/bin目录下运行如下命令 -├── report.mp4 report videm + neo4j-admin load --from="C:\Users\Zarc\Downloads\data\neo4j.dump" --database=neo4j --force -└── report.ppt report slides +## 启动服务 +1. 在neo4j的/bin目录运行 + + neo4j.bat console + +2. 在./code/backend目录运行 + + npm run dev + +3. 在./code/frontend目录运行 + + python -m http.server 8000 + +4. 从8000端口访问管理界面 \ No newline at end of file diff --git a/code/backend/README.md b/code/backend/README.md deleted file mode 100644 index c25d301..0000000 --- a/code/backend/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# 知识图谱后端服务 - -## 环境要求 - jdk 11 - nodejs 22.19.0 - angular_cli 20.3.2 - neo4j 4.4 - express 4.18.2 - - -## 安装依赖 -在 ./code/backend 目录运行 - - npm install - npm install express@4.18.2 - -## 启动服务 -在neo4j的/bin目录运行 - - neo4j.bat start - -./code/backend目录运行 - - npm run dev - -./code/frontend目录运行 - - python -m http.server 8000 \ No newline at end of file diff --git a/code/frontend/README.md b/code/frontend/README.md deleted file mode 100644 index e1ecac7..0000000 --- a/code/frontend/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Frontend - -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.2. - -## Development server - -To start a local development server, run: - -```bash -ng serve -``` - -Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. - -## Code scaffolding - -Angular CLI includes powerful code scaffolding tools. To generate a new component, run: - -```bash -ng generate component component-name -``` - -For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: - -```bash -ng generate --help -``` - -## Building - -To build the project run: - -```bash -ng build -``` - -This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. - -## Running unit tests - -To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: - -```bash -ng test -``` - -## Running end-to-end tests - -For end-to-end (e2e) testing, run: - -```bash -ng e2e -``` - -Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. - -## Additional Resources - -For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. From b55b136c103172f555c4ec801ec2ede625c660a3 Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Thu, 25 Sep 2025 07:18:56 +0800 Subject: [PATCH 02/11] fix the issues of limited number of nodes --- code/backend/routes/edges.js | 16 +++-- code/backend/routes/nodes.js | 16 +++-- code/backend/services/edge.service.js | 38 +++++++---- code/backend/services/node.service.js | 36 ++++++---- code/frontend/api-service.js | 30 ++++++--- code/frontend/app.js | 95 ++++++++++++++++++++++----- code/frontend/index.html | 8 +++ 7 files changed, 180 insertions(+), 59 deletions(-) diff --git a/code/backend/routes/edges.js b/code/backend/routes/edges.js index 418d3be..d07ea79 100644 --- a/code/backend/routes/edges.js +++ b/code/backend/routes/edges.js @@ -11,11 +11,14 @@ const edgeService = require('../services/edge.service'); router.get('/', async (req, res) => { try { const { - limit = 100, + limit = 10000, // 修改默认值为10000 skip = 0, type } = req.query; + // 添加调试日志 + console.log(`边路由接收参数: limit=${limit}, skip=${skip}, type=${type}`); + let edges; if (type) { @@ -36,7 +39,12 @@ router.get('/', async (req, res) => { res.json({ success: true, count: edges.length, - data: edges + data: edges, + meta: { + limit: parseInt(limit), + skip: parseInt(skip), + total: edges.length + } }); } catch (error) { console.error('获取关系列表失败:', error); @@ -200,7 +208,7 @@ router.delete('/:id', async (req, res) => { router.get('/type/:type', async (req, res) => { try { const { type } = req.params; - const { limit = 100, skip = 0 } = req.query; + const { limit = 10000, skip = 0 } = req.query; // 修改默认值 const edges = await edgeService.getEdgesByType( type, @@ -233,7 +241,7 @@ router.get('/node/:nodeId', async (req, res) => { const { nodeId } = req.params; const { direction = 'both', - limit = 100, + limit = 10000, // 修改默认值 skip = 0 } = req.query; diff --git a/code/backend/routes/nodes.js b/code/backend/routes/nodes.js index 8d715f7..d6e6459 100644 --- a/code/backend/routes/nodes.js +++ b/code/backend/routes/nodes.js @@ -11,11 +11,14 @@ const nodeService = require('../services/node.service'); router.get('/', async (req, res) => { try { const { - limit = 100, + limit = 10000, // 修改默认值为10000 skip = 0, label } = req.query; + // 添加调试日志 + console.log(`节点路由接收参数: limit=${limit}, skip=${skip}, label=${label}`); + let nodes; if (label) { @@ -36,7 +39,12 @@ router.get('/', async (req, res) => { res.json({ success: true, count: nodes.length, - data: nodes + data: nodes, + meta: { + limit: parseInt(limit), + skip: parseInt(skip), + total: nodes.length + } }); } catch (error) { console.error('获取节点列表失败:', error); @@ -195,7 +203,7 @@ router.delete('/:id', async (req, res) => { router.get('/search/:key/:value', async (req, res) => { try { const { key, value } = req.params; - const { limit = 100, skip = 0 } = req.query; + const { limit = 10000, skip = 0 } = req.query; // 修改默认值 const nodes = await nodeService.searchNodes( key, @@ -227,7 +235,7 @@ router.get('/search/:key/:value', async (req, res) => { router.get('/label/:label', async (req, res) => { try { const { label } = req.params; - const { limit = 100, skip = 0 } = req.query; + const { limit = 10000, skip = 0 } = req.query; // 修改默认值 const nodes = await nodeService.getNodesByLabel( label, diff --git a/code/backend/services/edge.service.js b/code/backend/services/edge.service.js index 045e8ab..0940d75 100644 --- a/code/backend/services/edge.service.js +++ b/code/backend/services/edge.service.js @@ -13,21 +13,35 @@ class EdgeService { * @param {number} skip - 跳过数量 * @returns {Promise} 关系列表 */ - async getAllEdges(limit = 100, skip = 0) { + /** + * 获取所有关系(带极高上限) + * @param {number} limit - 每页数量(前端控制) + * @param {number} skip - 跳过数量 + * @returns {Promise} 关系列表 + */ + async getAllEdges(limit = 10000, skip = 0) { try { + // 强制使用传入的参数 + const actualLimit = limit; + const actualSkip = skip; + + // 设置安全上限 + const safeLimit = Math.min(actualLimit, 10000); + const query = ` - MATCH ()-[r]->() - RETURN r, startNode(r) as start, endNode(r) as end - SKIP $skip - LIMIT $limit - `; + MATCH ()-[r]->() + RETURN r, startNode(r) as start, endNode(r) as end + SKIP $skip + LIMIT $limit + `; - // const params = { skip, limit }; - // 确保参数是整数 const params = { - skip: int(parseInt(skip)), - limit: int(parseInt(limit)) + skip: int(parseInt(actualSkip)), + limit: int(parseInt(safeLimit)) }; + + console.log(`边服务: skip=${actualSkip}, limit=${safeLimit} (传入limit=${limit})`); + const result = await neo4j.read(query, params); return result.records.map(record => { @@ -80,7 +94,7 @@ class EdgeService { * @param {number} skip - 跳过数量 * @returns {Promise} 关系列表 */ - async getEdgesByType(type, limit = 100, skip = 0) { + async getEdgesByType(type, limit = 10000, skip = 0) { try { const query = ` MATCH ()-[r:${type}]->() @@ -217,7 +231,7 @@ class EdgeService { * @param {number} skip - 跳过数量 * @returns {Promise} 关系列表 */ - async getNodeEdges(nodeId, direction = 'both', limit = 100, skip = 0) { + async getNodeEdges(nodeId, direction = 'both', limit = 10000, skip = 0) { try { let pattern; diff --git a/code/backend/services/node.service.js b/code/backend/services/node.service.js index 9f25bca..f2a84a5 100644 --- a/code/backend/services/node.service.js +++ b/code/backend/services/node.service.js @@ -8,26 +8,34 @@ const { v4: uuidv4 } = require('uuid'); // 用于生成唯一ID */ class NodeService { /** - * 获取所有节点(带分页) - * @param {number} limit - 每页数量 + * 获取所有节点(带极高上限) + * @param {number} limit - 每页数量(前端控制) * @param {number} skip - 跳过数量 * @returns {Promise} 节点列表 */ - async getAllNodes(limit = 100, skip = 0) { + async getAllNodes(limit = 10000, skip = 0) { try { + // 强制使用传入的参数,不使用默认值 + const actualLimit = limit; + const actualSkip = skip; + + // 设置安全上限 + const safeLimit = Math.min(actualLimit, 10000); + const query = ` - MATCH (n) - RETURN n - SKIP $skip - LIMIT $limit - `; + MATCH (n) + RETURN n + SKIP $skip + LIMIT $limit + `; - // const params = { skip, limit }; - // 确保参数是整数 const params = { - skip: int(parseInt(skip)), - limit: int(parseInt(limit)) + skip: int(parseInt(actualSkip)), + limit: int(parseInt(safeLimit)) }; + + console.log(`节点服务: skip=${actualSkip}, limit=${safeLimit} (传入limit=${limit})`); + const result = await neo4j.read(query, params); return result.records.map(record => { @@ -74,7 +82,7 @@ class NodeService { * @param {number} skip - 跳过数量 * @returns {Promise} 节点列表 */ - async getNodesByLabel(label, limit = 100, skip = 0) { + async getNodesByLabel(label, limit = 10000, skip = 0) { try { const query = ` MATCH (n:${label}) @@ -198,7 +206,7 @@ class NodeService { * @param {number} skip - 跳过数量 * @returns {Promise} 匹配的节点列表 */ - async searchNodes(key, value, limit = 100, skip = 0) { + async searchNodes(key, value, limit = 10000, skip = 0) { try { const query = ` MATCH (n) diff --git a/code/frontend/api-service.js b/code/frontend/api-service.js index b462475..b30fdf4 100644 --- a/code/frontend/api-service.js +++ b/code/frontend/api-service.js @@ -3,6 +3,7 @@ class ApiService { this.baseUrl = 'http://localhost:3000/api'; } + async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; @@ -32,11 +33,17 @@ class ApiService { } } - // 节点相关API - async getNodes(limit = 100, skip = 0, label = null) { - const params = new URLSearchParams({ limit, skip }); + // 节点相关API - 修复参数传递 + async getNodes(limit = 10000, skip = 0, label = null) { + const params = new URLSearchParams({ + limit: limit.toString(), + skip: skip.toString() + }); if (label) params.append('label', label); + console.log("API请求节点,参数:", { limit, skip, label }); + console.log("请求URL:", `/nodes?${params}`); + return this.request(`/nodes?${params}`); } @@ -82,14 +89,21 @@ class ApiService { }); } - // 关系相关API - async getEdges(limit = 100, skip = 0, type = null) { - const params = new URLSearchParams({ limit, skip }); + // 关系相关API - 修复参数传递 + async getEdges(limit = 10000, skip = 0, type = null) { + const params = new URLSearchParams({ + limit: limit.toString(), + skip: skip.toString() + }); if (type) params.append('type', type); + console.log("API请求关系,参数:", { limit, skip, type }); + console.log("请求URL:", `/edges?${params}`); + return this.request(`/edges?${params}`); } + async getEdge(id) { try { console.log("请求关系详情,ID:", id); @@ -134,12 +148,12 @@ class ApiService { } // 搜索功能 - async searchNodes(key, value, limit = 100, skip = 0) { + async searchNodes(key, value, limit = 10000, skip = 0) { const params = new URLSearchParams({ limit, skip }); return this.request(`/nodes/search/${key}/${value}?${params}`); } - async getNodeEdges(nodeId, direction = 'both', limit = 100, skip = 0) { + async getNodeEdges(nodeId, direction = 'both', limit = 10000, skip = 0) { const params = new URLSearchParams({ direction, limit, skip }); return this.request(`/edges/node/${nodeId}?${params}`); } diff --git a/code/frontend/app.js b/code/frontend/app.js index 9d54279..e419ccd 100644 --- a/code/frontend/app.js +++ b/code/frontend/app.js @@ -8,6 +8,12 @@ class KnowledgeGraphApp { edges: [] }; + // 添加配置项 + this.config = { + maxNodes: 10000, // 默认最大显示50个节点 + maxEdges: 10000 // 可选:也可以控制最大关系数 + }; + this.init(); console.log("KnowledgeGraphApp 初始化完成"); } @@ -69,6 +75,17 @@ class KnowledgeGraphApp { this.hideModal(); }); + // 添加调试控件事件绑定 + bindEventWhenReady('#debug-apply-limit', 'click', () => { + const limitInput = document.getElementById('debug-node-limit'); + const maxNodes = parseInt(limitInput.value); + if (!isNaN(maxNodes) && maxNodes > 0) { + this.config.maxNodes = maxNodes; + console.log(`手动设置最大节点数为: ${maxNodes}`); + this.loadData(); + } + }); + // 点击模态框外部关闭 window.addEventListener('click', (event) => { const modal = document.getElementById('modal'); @@ -83,23 +100,47 @@ class KnowledgeGraphApp { async loadData() { try { console.log("开始加载数据..."); + console.log("请求参数 - maxNodes:", this.config.maxNodes, "maxEdges:", this.config.maxEdges); - // 同时获取节点和关系 + // 使用配置的最大节点数 const [nodesResponse, edgesResponse] = await Promise.all([ - this.api.getNodes(), - this.api.getEdges() + this.api.getNodes(this.config.maxNodes), + this.api.getEdges(this.config.maxEdges) ]); - console.log("节点响应:", nodesResponse); - console.log("关系响应:", edgesResponse); + // 检查响应结构 + console.log("节点响应结构:", nodesResponse); + console.log("关系响应结构:", edgesResponse); + + console.log("节点响应数据量:", nodesResponse.data ? nodesResponse.data.length : 0); + console.log("关系响应数据量:", edgesResponse.data ? edgesResponse.data.length : 0); // 确保数据存在 this.currentData.nodes = nodesResponse.data || []; this.currentData.edges = edgesResponse.data || []; - // 打印所有节点ID以供调试 - console.log("所有节点ID:", this.currentData.nodes.map(n => n.id)); - console.log("所有关系ID:", this.currentData.edges.map(e => e.id)); + // 诊断信息 + console.log("=== 数据诊断 ==="); + console.log("配置限制 - 节点:", this.config.maxNodes, "边:", this.config.maxEdges); + console.log("实际获取 - 节点:", this.currentData.nodes.length, "边:", this.currentData.edges.length); + + if (this.currentData.nodes.length === this.config.maxNodes) { + console.log("✅ 节点数量达到限制,后端限制生效"); + } else { + console.log("⚠️ 节点数量未达到限制,可能原因:"); + console.log(" - 数据库中没有足够节点"); + console.log(" - 后端服务未正确处理limit参数"); + console.log(" - 网络请求参数错误"); + } + + // 如果返回的节点数超过限制,进行截断 + if (this.currentData.nodes.length > this.config.maxNodes) { + console.warn(`节点数量超过限制,显示前 ${this.config.maxNodes} 个节点`); + this.currentData.nodes = this.currentData.nodes.slice(0, this.config.maxNodes); + } + + console.log("最终显示节点数量:", this.currentData.nodes.length); + console.log("最终显示关系数量:", this.currentData.edges.length); this.renderSidebarLists(); this.graphRenderer.render(this.currentData); @@ -111,11 +152,21 @@ class KnowledgeGraphApp { } } + // 添加设置最大节点数的方法 + setMaxNodes(maxNodes) { + if (maxNodes > 0 && maxNodes <= 1000) { // 设置合理范围 + this.config.maxNodes = maxNodes; + console.log(`设置最大节点数为: ${maxNodes}`); + this.loadData(); // 重新加载数据 + } else { + alert('最大节点数必须在1-1000之间'); + } + } + debugInfo() { console.log("=== 调试信息开始 ==="); - console.log("当前数据:", this.currentData); - console.log("节点数量:", this.currentData.nodes.length); - console.log("关系数量:", this.currentData.edges.length); + console.log("当前配置:", this.config); + console.log("最大节点数:", this.config.maxNodes); // 检查API服务状态 console.log("API基础URL:", this.api.baseUrl); @@ -157,15 +208,28 @@ class KnowledgeGraphApp { return id && typeof id === 'string' && id.length > 0; } - // 修改 renderSidebarLists 方法中的节点点击处理 renderSidebarLists() { - // 渲染节点列表 const nodesContainer = document.getElementById('nodes-container'); + const edgesContainer = document.getElementById('edges-container'); + + // 清空容器 nodesContainer.innerHTML = ''; + edgesContainer.innerHTML = ''; + // 添加统计信息 + const statsHtml = ` +
+ 统计信息:
+ 节点: ${this.currentData.nodes.length}/${this.config.maxNodes}
+ 关系: ${this.currentData.edges.length}/${this.config.maxEdges} +
+ `; + + nodesContainer.innerHTML = statsHtml; + + // 渲染节点列表 this.currentData.nodes.forEach(node => { const li = document.createElement('li'); - // 使用更安全的显示方式 const displayName = node.properties && node.properties.name ? node.properties.name : `节点 ${node.id ? node.id.substring(0, 8) : '未知'}`; @@ -180,9 +244,6 @@ class KnowledgeGraphApp { }); // 渲染关系列表 - const edgesContainer = document.getElementById('edges-container'); - edgesContainer.innerHTML = ''; - this.currentData.edges.forEach(edge => { const li = document.createElement('li'); const startName = edge.startNode && edge.startNode.properties && edge.startNode.properties.name diff --git a/code/frontend/index.html b/code/frontend/index.html index a6375a9..ba52b76 100644 --- a/code/frontend/index.html +++ b/code/frontend/index.html @@ -14,10 +14,18 @@

知识图谱管理系统

+
+ + +
+ + +
+
From 71db808d16958d43cf2e63c8f5a63ec5dc6cc363 Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Thu, 25 Sep 2025 08:50:57 +0800 Subject: [PATCH 03/11] extended the limitation functions to edges --- code/frontend/app.js | 58 ++++++++++++++++++++++++++++++++++------ code/frontend/index.html | 14 +++++++--- code/frontend/style.css | 32 +++++++++++++++++++--- 3 files changed, 90 insertions(+), 14 deletions(-) diff --git a/code/frontend/app.js b/code/frontend/app.js index e419ccd..f2d4b5b 100644 --- a/code/frontend/app.js +++ b/code/frontend/app.js @@ -75,7 +75,7 @@ class KnowledgeGraphApp { this.hideModal(); }); - // 添加调试控件事件绑定 + // 添加节点数控制事件绑定 bindEventWhenReady('#debug-apply-limit', 'click', () => { const limitInput = document.getElementById('debug-node-limit'); const maxNodes = parseInt(limitInput.value); @@ -86,6 +86,17 @@ class KnowledgeGraphApp { } }); + // 添加边数控制事件绑定 - 添加这行代码 + bindEventWhenReady('#debug-apply-edge-limit', 'click', () => { + const limitInput = document.getElementById('debug-edge-limit'); + const maxEdges = parseInt(limitInput.value); + if (!isNaN(maxEdges) && maxEdges > 0) { + this.config.maxEdges = maxEdges; + console.log(`手动设置最大边数为: ${maxEdges}`); + this.loadData(); + } + }); + // 点击模态框外部关闭 window.addEventListener('click', (event) => { const modal = document.getElementById('modal'); @@ -139,6 +150,12 @@ class KnowledgeGraphApp { this.currentData.nodes = this.currentData.nodes.slice(0, this.config.maxNodes); } + // 如果返回的边数超过限制,进行截断 - 添加这行代码 + if (this.currentData.edges.length > this.config.maxEdges) { + console.warn(`边数量超过限制,显示前 ${this.config.maxEdges} 条边`); + this.currentData.edges = this.currentData.edges.slice(0, this.config.maxEdges); + } + console.log("最终显示节点数量:", this.currentData.nodes.length); console.log("最终显示关系数量:", this.currentData.edges.length); @@ -154,12 +171,23 @@ class KnowledgeGraphApp { // 添加设置最大节点数的方法 setMaxNodes(maxNodes) { - if (maxNodes > 0 && maxNodes <= 1000) { // 设置合理范围 + if (maxNodes > 0 && maxNodes <= 10000) { // 设置合理范围 this.config.maxNodes = maxNodes; console.log(`设置最大节点数为: ${maxNodes}`); this.loadData(); // 重新加载数据 } else { - alert('最大节点数必须在1-1000之间'); + alert('最大节点数必须在1-10000之间'); + } + } + + // 添加设置最大边数的方法 + setMaxEdges(maxEdges) { + if (maxEdges > 0 && maxEdges <= 10000) { + this.config.maxEdges = maxEdges; + console.log(`设置最大边数为: ${maxEdges}`); + this.loadData(); + } else { + alert('最大边数必须在1-10000之间'); } } @@ -167,24 +195,38 @@ class KnowledgeGraphApp { console.log("=== 调试信息开始 ==="); console.log("当前配置:", this.config); console.log("最大节点数:", this.config.maxNodes); + console.log("最大边数:", this.config.maxEdges); // 检查API服务状态 console.log("API基础URL:", this.api.baseUrl); - // 测试API连接 - 使用更简单的方式 + // 测试API连接 console.log("测试API连接..."); // 直接测试API端点 fetch(`${this.api.baseUrl}/nodes?limit=1`) .then(response => { - console.log("API响应状态:", response.status, response.statusText); + console.log("API节点响应状态:", response.status, response.statusText); + return response.json(); + }) + .then(data => { + console.log("API节点测试成功:", data); + }) + .catch(error => { + console.error("API节点测试失败:", error); + }); + + // 测试边API端点 + fetch(`${this.api.baseUrl}/edges?limit=1`) + .then(response => { + console.log("API边响应状态:", response.status, response.statusText); return response.json(); }) .then(data => { - console.log("API测试成功:", data); + console.log("API边测试成功:", data); }) .catch(error => { - console.error("API测试失败:", error); + console.error("API边测试失败:", error); }); // 检查图谱渲染器状态 @@ -192,7 +234,7 @@ class KnowledgeGraphApp { console.log("图谱实例:", this.graphRenderer.graph ? "已初始化" : "未初始化"); // 检查所有按钮状态 - const buttons = ['refresh-btn', 'add-node-btn', 'add-edge-btn', 'debug-btn', 'search-btn']; + const buttons = ['refresh-btn', 'add-node-btn', 'add-edge-btn', 'debug-btn', 'search-btn', 'debug-apply-limit', 'debug-apply-edge-limit']; buttons.forEach(btnId => { const btn = document.getElementById(btnId); console.log(`按钮 ${btnId}:`, btn ? "存在" : "不存在"); diff --git a/code/frontend/index.html b/code/frontend/index.html index ba52b76..6c3aa99 100644 --- a/code/frontend/index.html +++ b/code/frontend/index.html @@ -20,10 +20,18 @@

知识图谱管理系统

- +
- - + 节点: + + +
+ + +
+ 边: + +
diff --git a/code/frontend/style.css b/code/frontend/style.css index 32c19d8..f4455a7 100644 --- a/code/frontend/style.css +++ b/code/frontend/style.css @@ -25,20 +25,46 @@ header { align-items: center; } +/* 在现有的controls样式基础上添加 */ + +.controls { + display: flex; + align-items: center; + gap: 10px; +} + +.controls>div { + display: flex; + align-items: center; + gap: 5px; +} + +.controls input[type="number"] { + width: 60px; + padding: 2px 5px; + border: 1px solid #ccc; + border-radius: 3px; +} + .controls button { background-color: #3498db; color: white; border: none; - padding: 0.5rem 1rem; - margin-left: 0.5rem; - border-radius: 4px; + padding: 4px 8px; + border-radius: 3px; cursor: pointer; + font-size: 12px; } .controls button:hover { background-color: #2980b9; } +.controls span { + color: white; + font-size: 12px; +} + .main-content { display: flex; flex: 1; From 7070db68668eb7f47e205094625ae3c823d8020a Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Thu, 25 Sep 2025 10:23:16 +0800 Subject: [PATCH 04/11] Added modification tips --- code/frontend/api-service.js | 14 ++++++- code/frontend/app.js | 79 +++++++++++++++++++++++++++++++----- code/frontend/style.css | 65 +++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 11 deletions(-) diff --git a/code/frontend/api-service.js b/code/frontend/api-service.js index b30fdf4..0c87b97 100644 --- a/code/frontend/api-service.js +++ b/code/frontend/api-service.js @@ -4,6 +4,7 @@ class ApiService { } + // 在 api-service.js 的 request 方法中添加响应格式检查 async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; @@ -26,7 +27,18 @@ class ApiService { throw new Error(`API请求失败: ${response.status} ${response.statusText} - ${errorText}`); } - return response.json(); + const result = await response.json(); + + // 检查响应格式(可选) + if (result && typeof result.success !== 'undefined') { + return result; + } else { + // 如果API没有返回success字段,包装一下 + return { + success: true, + data: result + }; + } } catch (error) { console.error('API请求错误:', error); throw error; diff --git a/code/frontend/app.js b/code/frontend/app.js index f2d4b5b..78f2d30 100644 --- a/code/frontend/app.js +++ b/code/frontend/app.js @@ -380,19 +380,26 @@ class KnowledgeGraphApp { properties.name = name; // 确保名称属性 + let result; if (existingNode) { console.log("更新节点:", existingNode.id, properties); - await this.api.updateNode(existingNode.id, properties); + result = await this.api.updateNode(existingNode.id, properties); } else { console.log("创建节点:", properties, labels); - await this.api.createNode({ properties, labels }); + result = await this.api.createNode({ properties, labels }); } - this.hideModal(); - this.loadData(); // 刷新数据 + // 检查API响应是否成功 + if (result && result.success) { + this.showSuccessMessage(existingNode ? '节点更新成功!' : '节点创建成功!'); + this.hideModal(); + this.loadData(); // 刷新数据 + } else { + throw new Error(result?.message || '操作失败,请检查服务器响应'); + } } catch (error) { console.error('保存节点失败:', error); - alert('保存节点失败: ' + error.message); + this.showErrorMessage('保存节点失败: ' + error.message); } } @@ -439,6 +446,51 @@ class KnowledgeGraphApp { }); } + // 显示成功消息 + showSuccessMessage(message) { + this.showMessage(message, 'success'); + } + + // 显示错误消息 + showErrorMessage(message) { + this.showMessage(message, 'error'); + } + + // 显示消息(支持成功和错误类型) + showMessage(message, type = 'info') { + // 移除已存在的消息框 + const existingMessage = document.getElementById('operation-message'); + if (existingMessage) { + existingMessage.remove(); + } + + // 创建消息元素 + const messageDiv = document.createElement('div'); + messageDiv.id = 'operation-message'; + messageDiv.className = `message ${type}`; + messageDiv.innerHTML = ` + ${message} + + `; + + // 添加到页面 + document.body.appendChild(messageDiv); + + // 添加关闭按钮事件 + document.getElementById('close-message-btn').addEventListener('click', () => { + messageDiv.remove(); + }); + + // 3秒后自动消失(仅对成功消息) + if (type === 'success') { + setTimeout(() => { + if (messageDiv.parentNode) { + messageDiv.remove(); + } + }, 3000); + } + } + async saveEdge(existingEdge) { try { const startNodeId = document.getElementById('edge-start').value; @@ -454,19 +506,26 @@ class KnowledgeGraphApp { throw new Error('属性JSON格式不正确'); } + let result; if (existingEdge) { console.log("更新关系:", existingEdge.id, properties); - await this.api.updateEdge(existingEdge.id, properties); + result = await this.api.updateEdge(existingEdge.id, properties); } else { console.log("创建关系:", startNodeId, endNodeId, type, properties); - await this.api.createEdge({ startNodeId, endNodeId, type, properties }); + result = await this.api.createEdge({ startNodeId, endNodeId, type, properties }); } - this.hideModal(); - this.loadData(); // 刷新数据 + // 检查API响应是否成功 + if (result && result.success) { + this.showSuccessMessage(existingEdge ? '关系更新成功!' : '关系创建成功!'); + this.hideModal(); + this.loadData(); // 刷新数据 + } else { + throw new Error(result?.message || '操作失败,请检查服务器响应'); + } } catch (error) { console.error('保存关系失败:', error); - alert('保存关系失败: ' + error.message); + this.showErrorMessage('保存关系失败: ' + error.message); } } diff --git a/code/frontend/style.css b/code/frontend/style.css index f4455a7..c9930c5 100644 --- a/code/frontend/style.css +++ b/code/frontend/style.css @@ -210,4 +210,69 @@ header { .form-actions button[type="button"] { background-color: #95a5a6; color: white; +} + +/* 操作消息样式 */ +.message { + position: fixed; + top: 20px; + right: 20px; + padding: 15px 20px; + border-radius: 4px; + color: white; + z-index: 1001; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + justify-content: space-between; + min-width: 300px; + max-width: 500px; + animation: slideIn 0.3s ease-out; +} + +.message.success { + background-color: #52c41a; + border-left: 4px solid #389e0d; +} + +.message.error { + background-color: #ff4d4f; + border-left: 4px solid #cf1322; +} + +.message.info { + background-color: #1890ff; + border-left: 4px solid #096dd9; +} + +#close-message-btn { + background: none; + border: none; + color: white; + font-size: 18px; + cursor: pointer; + margin-left: 10px; + padding: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; +} + +#close-message-btn:hover { + background-color: rgba(255, 255, 255, 0.2); + border-radius: 50%; +} + +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } } \ No newline at end of file From 206beba3522949c1320aa77bb33188ef462caa5e Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Thu, 25 Sep 2025 10:32:27 +0800 Subject: [PATCH 05/11] fixed the click problem --- code/frontend/app.js | 7 +++- code/frontend/graph-renderer.js | 69 ++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/code/frontend/app.js b/code/frontend/app.js index 78f2d30..721cddf 100644 --- a/code/frontend/app.js +++ b/code/frontend/app.js @@ -14,6 +14,9 @@ class KnowledgeGraphApp { maxEdges: 10000 // 可选:也可以控制最大关系数 }; + // 设置 GraphRenderer 的应用实例引用 + this.graphRenderer.setAppInstance(this); + this.init(); console.log("KnowledgeGraphApp 初始化完成"); } @@ -698,10 +701,10 @@ class KnowledgeGraphApp { // 初始化应用 document.addEventListener('DOMContentLoaded', () => { - new KnowledgeGraphApp(); + const app = new KnowledgeGraphApp(); + window.app = app; // 设置为全局变量 }); -// 在文件末尾添加 // 全局错误处理 window.addEventListener('error', function (e) { console.error('全局错误:', e.error); diff --git a/code/frontend/graph-renderer.js b/code/frontend/graph-renderer.js index d502758..db1e2c6 100644 --- a/code/frontend/graph-renderer.js +++ b/code/frontend/graph-renderer.js @@ -2,9 +2,15 @@ class GraphRenderer { constructor(containerId) { this.containerId = containerId; this.graph = null; + this.app = null; // 添加应用实例引用 this.init(); } + // 设置应用实例的方法 + setAppInstance(app) { + this.app = app; + } + init() { const container = document.getElementById(this.containerId); const width = container.clientWidth; @@ -93,18 +99,42 @@ class GraphRenderer { this.graph.render(); this.graph.fitView(); - // 添加节点点击事件 + // 绑定点击事件 + this.bindGraphEvents(); + } + + bindGraphEvents() { + // 移除旧的事件监听器 + this.graph.off('node:click'); + this.graph.off('edge:click'); + + // 绑定新的事件 this.graph.on('node:click', (evt) => { const node = evt.item; const nodeId = node.get('model').id; - document.dispatchEvent(new CustomEvent('nodeClick', { detail: nodeId })); + console.log('节点被点击:', nodeId); + + if (this.app && this.app.showNodeDetail) { + this.app.showNodeDetail(nodeId); + } else { + console.error('应用实例未找到或showNodeDetail方法不存在'); + // 备用方案:使用全局事件 + document.dispatchEvent(new CustomEvent('nodeClick', { detail: nodeId })); + } }); - // 添加边点击事件 this.graph.on('edge:click', (evt) => { const edge = evt.item; const edgeId = edge.get('model').id; - document.dispatchEvent(new CustomEvent('edgeClick', { detail: edgeId })); + console.log('边被点击:', edgeId); + + if (this.app && this.app.showEdgeDetail) { + this.app.showEdgeDetail(edgeId); + } else { + console.error('应用实例未找到或showEdgeDetail方法不存在'); + // 备用方案:使用全局事件 + document.dispatchEvent(new CustomEvent('edgeClick', { detail: edgeId })); + } }); } @@ -139,14 +169,33 @@ class GraphRenderer { } } -// 监听节点和边点击事件 +// // 监听节点和边点击事件 +// document.addEventListener('nodeClick', (e) => { +// // 在实际应用中,这里可以调用应用实例的方法 +// console.log('节点被点击:', e.detail); +// // 例如: app.showNodeDetail(e.detail); +// }); + +// document.addEventListener('edgeClick', (e) => { +// console.log('边被点击:', e.detail); +// // 例如: app.showEdgeDetail(e.detail); +// }); + +// 备用方案:通过全局事件处理 document.addEventListener('nodeClick', (e) => { - // 在实际应用中,这里可以调用应用实例的方法 - console.log('节点被点击:', e.detail); - // 例如: app.showNodeDetail(e.detail); + console.log('通过全局事件处理节点点击:', e.detail); + if (window.app && window.app.showNodeDetail) { + window.app.showNodeDetail(e.detail); + } else { + console.warn('无法处理节点点击事件:应用实例未找到'); + } }); document.addEventListener('edgeClick', (e) => { - console.log('边被点击:', e.detail); - // 例如: app.showEdgeDetail(e.detail); + console.log('通过全局事件处理边点击:', e.detail); + if (window.app && window.app.showEdgeDetail) { + window.app.showEdgeDetail(e.detail); + } else { + console.warn('无法处理边点击事件:应用实例未找到'); + } }); \ No newline at end of file From f9d2e437bc1ba589341026f8c64cff2915ee2553 Mon Sep 17 00:00:00 2001 From: Zarc-X <2793215474@qq.com> Date: Wed, 1 Oct 2025 20:09:01 +0800 Subject: [PATCH 06/11] modified some annotation --- .gitignore | 1 + README.md | 4 +- code/backend/routes/edges.js | 13 +- code/backend/routes/index.js | 10 - code/backend/routes/nodes.js | 6 +- code/backend/server.js | 8 +- code/backend/services/edge.service.js | 38 +- code/backend/services/neo4j.js | 10 +- code/backend/services/node.service.js | 37 +- code/frontend/api-service.js | 13 +- code/frontend/app.js | 551 +++++++++++++------------- code/frontend/graph-renderer.js | 28 +- code/frontend/index.html | 5 - 13 files changed, 338 insertions(+), 386 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17312bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/data/neo4j.dump diff --git a/README.md b/README.md index 78b3285..3215d24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 知识图谱后端服务 +# 知识图谱管理系统 ## 环境要求 jdk 11 @@ -19,7 +19,7 @@ 1. 确保neo4j处于停机状态 2. 在neo4j/bin目录下运行如下命令 - neo4j-admin load --from="C:\Users\Zarc\Downloads\data\neo4j.dump" --database=neo4j --force + neo4j-admin load --from="" --database=neo4j --force ## 启动服务 1. 在neo4j的/bin目录运行 diff --git a/code/backend/routes/edges.js b/code/backend/routes/edges.js index d07ea79..155702c 100644 --- a/code/backend/routes/edges.js +++ b/code/backend/routes/edges.js @@ -1,4 +1,3 @@ -// routes/edges.js const express = require('express'); const router = express.Router(); const edgeService = require('../services/edge.service'); @@ -11,25 +10,22 @@ const edgeService = require('../services/edge.service'); router.get('/', async (req, res) => { try { const { - limit = 10000, // 修改默认值为10000 + limit = 10000, skip = 0, type } = req.query; - // 添加调试日志 console.log(`边路由接收参数: limit=${limit}, skip=${skip}, type=${type}`); let edges; if (type) { - // 如果提供了类型参数,按类型过滤 edges = await edgeService.getEdgesByType( type, parseInt(limit), parseInt(skip) ); } else { - // 否则获取所有关系 edges = await edgeService.getAllEdges( parseInt(limit), parseInt(skip) @@ -96,7 +92,6 @@ router.post('/', async (req, res) => { try { const { startNodeId, endNodeId, type, properties } = req.body; - // 验证必要参数 if (!startNodeId || !endNodeId || !type) { return res.status(400).json({ success: false, @@ -136,7 +131,6 @@ router.put('/:id', async (req, res) => { const { id } = req.params; const { properties } = req.body; - // 验证必要参数 if (!properties) { return res.status(400).json({ success: false, @@ -208,7 +202,7 @@ router.delete('/:id', async (req, res) => { router.get('/type/:type', async (req, res) => { try { const { type } = req.params; - const { limit = 10000, skip = 0 } = req.query; // 修改默认值 + const { limit = 10000, skip = 0 } = req.query; const edges = await edgeService.getEdgesByType( type, @@ -241,11 +235,10 @@ router.get('/node/:nodeId', async (req, res) => { const { nodeId } = req.params; const { direction = 'both', - limit = 10000, // 修改默认值 + limit = 10000, skip = 0 } = req.query; - // 验证方向参数 const validDirections = ['incoming', 'outgoing', 'both']; if (!validDirections.includes(direction)) { return res.status(400).json({ diff --git a/code/backend/routes/index.js b/code/backend/routes/index.js index 9c74d0a..e1ea4a1 100644 --- a/code/backend/routes/index.js +++ b/code/backend/routes/index.js @@ -1,11 +1,9 @@ const express = require('express'); const router = express.Router(); -// 引入各个子路由模块 const nodesRouter = require('./nodes'); const edgesRouter = require('./edges'); -// 根路径欢迎信息 router.get('/', (req, res) => { res.json({ success: true, @@ -20,7 +18,6 @@ router.get('/', (req, res) => { }); }); -// 健康检查端点 router.get('/health', (req, res) => { res.json({ success: true, @@ -30,7 +27,6 @@ router.get('/health', (req, res) => { }); }); -// API信息端点 router.get('/api', (req, res) => { res.json({ success: true, @@ -59,14 +55,10 @@ router.get('/api', (req, res) => { }); }); -// 使用节点路由 router.use('/nodes', nodesRouter); -// 使用边路由 router.use('/edges', edgesRouter); -// 404处理 - 使用更兼容的方式处理不存在的API端点 -// 为所有可能的路径定义处理程序,而不是使用通配符 router.get('*', (req, res) => { handle404(req, res); }); @@ -83,7 +75,6 @@ router.delete('*', (req, res) => { handle404(req, res); }); -// 404处理函数 function handle404(req, res) { res.status(404).json({ success: false, @@ -98,7 +89,6 @@ function handle404(req, res) { }); } -// 错误处理中间件 router.use((err, req, res, next) => { console.error('服务器错误:', err); res.status(500).json({ diff --git a/code/backend/routes/nodes.js b/code/backend/routes/nodes.js index d6e6459..ca2cf09 100644 --- a/code/backend/routes/nodes.js +++ b/code/backend/routes/nodes.js @@ -1,4 +1,3 @@ -// routes/nodes.js const express = require('express'); const router = express.Router(); const nodeService = require('../services/node.service'); @@ -131,7 +130,6 @@ router.put('/:id', async (req, res) => { const { id } = req.params; const { properties } = req.body; - // 验证必要参数 if (!properties) { return res.status(400).json({ success: false, @@ -203,7 +201,7 @@ router.delete('/:id', async (req, res) => { router.get('/search/:key/:value', async (req, res) => { try { const { key, value } = req.params; - const { limit = 10000, skip = 0 } = req.query; // 修改默认值 + const { limit = 10000, skip = 0 } = req.query; const nodes = await nodeService.searchNodes( key, @@ -235,7 +233,7 @@ router.get('/search/:key/:value', async (req, res) => { router.get('/label/:label', async (req, res) => { try { const { label } = req.params; - const { limit = 10000, skip = 0 } = req.query; // 修改默认值 + const { limit = 10000, skip = 0 } = req.query; const nodes = await nodeService.getNodesByLabel( label, diff --git a/code/backend/server.js b/code/backend/server.js index 442c8b6..bd48c79 100644 --- a/code/backend/server.js +++ b/code/backend/server.js @@ -1,7 +1,7 @@ -require('dotenv').config(); // 加载.env环境变量 +require('dotenv').config(); const express = require('express'); const cors = require('cors'); -const routes = require('./routes'); // 导入所有路由 +const routes = require('./routes'); const app = express(); const PORT = process.env.PORT || 3000; @@ -28,8 +28,7 @@ app.use(cors({ })); app.use(express.json()); // 解析JSON请求体 -// 路由 -app.use('/api', routes); // 所有API路由都以/api开头 +app.use('/api', routes); // 全局错误处理中间件 app.use((err, req, res, next) => { @@ -41,7 +40,6 @@ app.use((err, req, res, next) => { }); }); -// 启动服务器 app.listen(PORT, () => { console.log(`后端服务器已启动在 http://localhost:${PORT}`); }); \ No newline at end of file diff --git a/code/backend/services/edge.service.js b/code/backend/services/edge.service.js index 0940d75..e015c13 100644 --- a/code/backend/services/edge.service.js +++ b/code/backend/services/edge.service.js @@ -63,15 +63,27 @@ class EdgeService { */ async getEdgeById(id) { try { - const query = ` - MATCH ()-[r {id: $id}]->() - RETURN r, startNode(r) as start, endNode(r) as end - `; + console.log(`边服务: 根据ID获取关系, ID: ${id}, 类型: ${typeof id}`); + + // 尝试两种查询方式:使用id属性或Neo4j内部ID + let query = ` + MATCH ()-[r]->() + WHERE r.id = $id OR ID(r) = $neo4jId + RETURN r, startNode(r) as start, endNode(r) as end + `; + + const params = { + id: id, + neo4jId: int(parseInt(id)) // 同时尝试作为Neo4j内部ID查询 + }; + + console.log('查询参数:', params); - const params = { id }; const result = await neo4j.read(query, params); + console.log(`查询结果记录数: ${result.records.length}`); if (result.records.length === 0) { + console.log(`未找到关系,ID: ${id}`); return null; } @@ -80,13 +92,15 @@ class EdgeService { const startNode = record.get('start'); const endNode = record.get('end'); - return this.formatEdge(relationship, startNode, endNode); + const formattedEdge = this.formatEdge(relationship, startNode, endNode); + console.log('格式化后的关系:', formattedEdge); + + return formattedEdge; } catch (error) { console.error('根据ID获取关系失败:', error); throw error; } } - /** * 根据类型获取关系 * @param {string} type - 关系类型 @@ -103,7 +117,6 @@ class EdgeService { LIMIT $limit `; - // const params = { skip, limit }; // 确保参数是整数 const params = { skip: int(parseInt(skip)), @@ -133,7 +146,6 @@ class EdgeService { */ async createEdge(startNodeId, endNodeId, type, properties = {}) { try { - // 生成唯一ID const id = uuidv4(); const edgeProperties = { ...properties, id }; @@ -215,7 +227,6 @@ class EdgeService { const params = { id }; const result = await neo4j.write(query, params); - // 检查是否有关系被删除 return result.summary.counters.updates().relationshipsDeleted > 0; } catch (error) { console.error('删除关系失败:', error); @@ -254,11 +265,6 @@ class EdgeService { LIMIT $limit `; - // const params = { - // nodeId, - // skip, - // limit - // }; // 确保参数是整数 const params = { nodeId, @@ -303,11 +309,9 @@ class EdgeService { labels: endNode.labels, properties: endNode.properties }, - // 如果需要,可以添加Neo4j内部ID neo4jId: relationship.identity.toString() }; } } -// 导出单例实例 module.exports = new EdgeService(); \ No newline at end of file diff --git a/code/backend/services/neo4j.js b/code/backend/services/neo4j.js index 3d15118..1b5e152 100644 --- a/code/backend/services/neo4j.js +++ b/code/backend/services/neo4j.js @@ -2,18 +2,15 @@ const neo4j = require('neo4j-driver'); require('dotenv').config(); // 确保加载环境变量 -// 数据库连接配置 - 使用默认的本地Neo4j设置 const URI = process.env.NEO4J_URI; const USER = process.env.NEO4J_USER; const PASSWORD = process.env.NEO4J_PASSWORD; -// 验证必要的环境变量 if (!URI || !USER || !PASSWORD) { console.error('缺少必要的数据库连接环境变量'); process.exit(1); } -// 创建驱动实例 let driver; /** @@ -28,7 +25,6 @@ function initDriver() { } catch (error) { console.error('Neo4j驱动初始化失败:', error); process.exit(1); // 退出进程 - // throw error; } } @@ -83,10 +79,8 @@ async function write(query, params = {}) { } } -// 初始化驱动 initDriver(); -// 导出功能 module.exports = { getDriver: () => driver, closeDriver: async () => { @@ -111,6 +105,6 @@ module.exports = { await session.close(); } }, - int: neo4j.int, // 导出neo4j.int函数 - isInt: neo4j.isInt // 导出neo4j.isInt函数 + int: neo4j.int, + isInt: neo4j.isInt }; \ No newline at end of file diff --git a/code/backend/services/node.service.js b/code/backend/services/node.service.js index f2a84a5..4683ce2 100644 --- a/code/backend/services/node.service.js +++ b/code/backend/services/node.service.js @@ -15,11 +15,9 @@ class NodeService { */ async getAllNodes(limit = 10000, skip = 0) { try { - // 强制使用传入的参数,不使用默认值 const actualLimit = limit; const actualSkip = skip; - // 设置安全上限 const safeLimit = Math.min(actualLimit, 10000); const query = ` @@ -91,7 +89,6 @@ class NodeService { LIMIT $limit `; - // const params = { skip, limit }; // 确保参数是整数 const params = { skip: int(parseInt(skip)), @@ -117,11 +114,9 @@ class NodeService { */ async createNode(properties, labels = []) { try { - // 生成唯一ID const id = uuidv4(); const nodeProperties = { ...properties, id }; - // 构建标签部分 const labelString = labels.length > 0 ? `:${labels.join(':')}` : ''; @@ -182,22 +177,33 @@ class NodeService { */ async deleteNode(id) { try { + console.log(`删除节点服务: ID=${id}, 类型=${typeof id}`); + + // 使用与 getNodeById 相同的查询逻辑 const query = ` - MATCH (n {id: $id}) - DETACH DELETE n - `; + MATCH (n) + WHERE n.id = $id OR ID(n) = $neo4jId + DETACH DELETE n + `; + + const params = { + id: id, + neo4jId: int(parseInt(id)) // 同时尝试作为Neo4j内部ID + }; + + console.log('删除节点参数:', params); - const params = { id }; const result = await neo4j.write(query, params); + const deletedCount = result.summary.counters.updates().nodesDeleted; + + console.log(`删除节点结果: 删除了 ${deletedCount} 个节点`); - // 检查是否有节点被删除 - return result.summary.counters.updates().nodesDeleted > 0; + return deletedCount > 0; } catch (error) { console.error('删除节点失败:', error); throw error; } } - /** * 搜索节点(根据属性值) * @param {string} key - 属性键 @@ -216,11 +222,6 @@ class NodeService { LIMIT $limit `; - // const params = { - // value, - // skip, - // limit - // }; // 确保参数是整数 const params = { value, @@ -250,11 +251,9 @@ class NodeService { id: node.properties.id || node.identity.toString(), labels: node.labels, properties: node.properties, - // 如果需要,可以添加Neo4j内部ID neo4jId: node.identity.toString() }; } } -// 导出单例实例 module.exports = new NodeService(); \ No newline at end of file diff --git a/code/frontend/api-service.js b/code/frontend/api-service.js index 0c87b97..e36b828 100644 --- a/code/frontend/api-service.js +++ b/code/frontend/api-service.js @@ -4,12 +4,10 @@ class ApiService { } - // 在 api-service.js 的 request 方法中添加响应格式检查 async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; try { - // 确保请求体是JSON字符串 if (options.body && typeof options.body !== 'string') { options.body = JSON.stringify(options.body); } @@ -29,11 +27,9 @@ class ApiService { const result = await response.json(); - // 检查响应格式(可选) if (result && typeof result.success !== 'undefined') { return result; } else { - // 如果API没有返回success字段,包装一下 return { success: true, data: result @@ -45,7 +41,7 @@ class ApiService { } } - // 节点相关API - 修复参数传递 + // 节点相关API async getNodes(limit = 10000, skip = 0, label = null) { const params = new URLSearchParams({ limit: limit.toString(), @@ -71,9 +67,7 @@ class ApiService { } } - // 修改创建节点方法 async createNode(nodeData) { - // 确保数据格式正确 const requestData = { properties: nodeData.properties || {}, labels: nodeData.labels || [] @@ -101,7 +95,7 @@ class ApiService { }); } - // 关系相关API - 修复参数传递 + // 关系相关API async getEdges(limit = 10000, skip = 0, type = null) { const params = new URLSearchParams({ limit: limit.toString(), @@ -128,9 +122,7 @@ class ApiService { } } - // 修改创建关系方法 async createEdge(edgeData) { - // 确保数据格式正确 const requestData = { startNodeId: edgeData.startNodeId, endNodeId: edgeData.endNodeId, @@ -159,7 +151,6 @@ class ApiService { }); } - // 搜索功能 async searchNodes(key, value, limit = 10000, skip = 0) { const params = new URLSearchParams({ limit, skip }); return this.request(`/nodes/search/${key}/${value}?${params}`); diff --git a/code/frontend/app.js b/code/frontend/app.js index 721cddf..d00c094 100644 --- a/code/frontend/app.js +++ b/code/frontend/app.js @@ -8,13 +8,11 @@ class KnowledgeGraphApp { edges: [] }; - // 添加配置项 this.config = { - maxNodes: 10000, // 默认最大显示50个节点 - maxEdges: 10000 // 可选:也可以控制最大关系数 + maxNodes: 10000, + maxEdges: 10000 }; - // 设置 GraphRenderer 的应用实例引用 this.graphRenderer.setAppInstance(this); this.init(); @@ -78,7 +76,7 @@ class KnowledgeGraphApp { this.hideModal(); }); - // 添加节点数控制事件绑定 + // 节点数控制事件 bindEventWhenReady('#debug-apply-limit', 'click', () => { const limitInput = document.getElementById('debug-node-limit'); const maxNodes = parseInt(limitInput.value); @@ -89,7 +87,7 @@ class KnowledgeGraphApp { } }); - // 添加边数控制事件绑定 - 添加这行代码 + // 边数控制事件 bindEventWhenReady('#debug-apply-edge-limit', 'click', () => { const limitInput = document.getElementById('debug-edge-limit'); const maxEdges = parseInt(limitInput.value); @@ -111,29 +109,30 @@ class KnowledgeGraphApp { console.log("所有事件绑定完成"); } + + /* + * 数据管理 + */ + // 加载所有数据 async loadData() { try { console.log("开始加载数据..."); console.log("请求参数 - maxNodes:", this.config.maxNodes, "maxEdges:", this.config.maxEdges); - // 使用配置的最大节点数 const [nodesResponse, edgesResponse] = await Promise.all([ this.api.getNodes(this.config.maxNodes), this.api.getEdges(this.config.maxEdges) ]); - // 检查响应结构 console.log("节点响应结构:", nodesResponse); console.log("关系响应结构:", edgesResponse); console.log("节点响应数据量:", nodesResponse.data ? nodesResponse.data.length : 0); console.log("关系响应数据量:", edgesResponse.data ? edgesResponse.data.length : 0); - // 确保数据存在 this.currentData.nodes = nodesResponse.data || []; this.currentData.edges = edgesResponse.data || []; - // 诊断信息 console.log("=== 数据诊断 ==="); console.log("配置限制 - 节点:", this.config.maxNodes, "边:", this.config.maxEdges); console.log("实际获取 - 节点:", this.currentData.nodes.length, "边:", this.currentData.edges.length); @@ -153,7 +152,7 @@ class KnowledgeGraphApp { this.currentData.nodes = this.currentData.nodes.slice(0, this.config.maxNodes); } - // 如果返回的边数超过限制,进行截断 - 添加这行代码 + // 如果返回的边数超过限制,进行截断 if (this.currentData.edges.length > this.config.maxEdges) { console.warn(`边数量超过限制,显示前 ${this.config.maxEdges} 条边`); this.currentData.edges = this.currentData.edges.slice(0, this.config.maxEdges); @@ -172,18 +171,18 @@ class KnowledgeGraphApp { } } - // 添加设置最大节点数的方法 + // 设置最大节点数 setMaxNodes(maxNodes) { - if (maxNodes > 0 && maxNodes <= 10000) { // 设置合理范围 + if (maxNodes > 0 && maxNodes <= 10000) { this.config.maxNodes = maxNodes; console.log(`设置最大节点数为: ${maxNodes}`); - this.loadData(); // 重新加载数据 + this.loadData(); } else { alert('最大节点数必须在1-10000之间'); } } - // 添加设置最大边数的方法 + // 设置最大边数 setMaxEdges(maxEdges) { if (maxEdges > 0 && maxEdges <= 10000) { this.config.maxEdges = maxEdges; @@ -194,140 +193,12 @@ class KnowledgeGraphApp { } } - debugInfo() { - console.log("=== 调试信息开始 ==="); - console.log("当前配置:", this.config); - console.log("最大节点数:", this.config.maxNodes); - console.log("最大边数:", this.config.maxEdges); - - // 检查API服务状态 - console.log("API基础URL:", this.api.baseUrl); - - // 测试API连接 - console.log("测试API连接..."); - - // 直接测试API端点 - fetch(`${this.api.baseUrl}/nodes?limit=1`) - .then(response => { - console.log("API节点响应状态:", response.status, response.statusText); - return response.json(); - }) - .then(data => { - console.log("API节点测试成功:", data); - }) - .catch(error => { - console.error("API节点测试失败:", error); - }); - - // 测试边API端点 - fetch(`${this.api.baseUrl}/edges?limit=1`) - .then(response => { - console.log("API边响应状态:", response.status, response.statusText); - return response.json(); - }) - .then(data => { - console.log("API边测试成功:", data); - }) - .catch(error => { - console.error("API边测试失败:", error); - }); - - // 检查图谱渲染器状态 - console.log("图谱容器:", this.graphRenderer.containerId); - console.log("图谱实例:", this.graphRenderer.graph ? "已初始化" : "未初始化"); - - // 检查所有按钮状态 - const buttons = ['refresh-btn', 'add-node-btn', 'add-edge-btn', 'debug-btn', 'search-btn', 'debug-apply-limit', 'debug-apply-edge-limit']; - buttons.forEach(btnId => { - const btn = document.getElementById(btnId); - console.log(`按钮 ${btnId}:`, btn ? "存在" : "不存在"); - }); - - console.log("=== 调试信息结束 ==="); - - // 显示简单提示 - alert("调试信息已输出到控制台,请按F12查看"); - } - - isValidId(id) { - return id && typeof id === 'string' && id.length > 0; - } - - renderSidebarLists() { - const nodesContainer = document.getElementById('nodes-container'); - const edgesContainer = document.getElementById('edges-container'); - - // 清空容器 - nodesContainer.innerHTML = ''; - edgesContainer.innerHTML = ''; - - // 添加统计信息 - const statsHtml = ` -
- 统计信息:
- 节点: ${this.currentData.nodes.length}/${this.config.maxNodes}
- 关系: ${this.currentData.edges.length}/${this.config.maxEdges} -
- `; - - nodesContainer.innerHTML = statsHtml; - - // 渲染节点列表 - this.currentData.nodes.forEach(node => { - const li = document.createElement('li'); - const displayName = node.properties && node.properties.name - ? node.properties.name - : `节点 ${node.id ? node.id.substring(0, 8) : '未知'}`; - - li.textContent = displayName; - li.dataset.id = node.id; - li.addEventListener('click', () => { - console.log("点击节点,ID:", node.id); - this.showNodeDetail(node.id); - }); - nodesContainer.appendChild(li); - }); - - // 渲染关系列表 - this.currentData.edges.forEach(edge => { - const li = document.createElement('li'); - const startName = edge.startNode && edge.startNode.properties && edge.startNode.properties.name - ? edge.startNode.properties.name - : (edge.startNodeId || '未知'); - const endName = edge.endNode && edge.endNode.properties && edge.endNode.properties.name - ? edge.endNode.properties.name - : (edge.endNodeId || '未知'); - - li.textContent = `${edge.type || '关系'}: ${startName} → ${endName}`; - li.dataset.id = edge.id; - li.addEventListener('click', () => { - console.log("点击关系,ID:", edge.id); - this.showEdgeDetail(edge.id); - }); - edgesContainer.appendChild(li); - }); - } - - async handleSearch() { - const query = document.getElementById('search-input').value.trim(); - if (!query) return; - - try { - // 简单搜索实现:搜索节点名称包含关键词的节点 - const response = await this.api.searchNodes('name', query); - - if (response.data.length > 0) { - // 高亮显示搜索结果 - this.graphRenderer.highlightNodes(response.data.map(node => node.id)); - } else { - alert('未找到匹配的节点'); - } - } catch (error) { - console.error('搜索失败:', error); - alert('搜索失败'); - } - } + /* + * UI 交互 + */ + // 显示表单 + // 编辑时传入节点对象,添加时传入null showNodeForm(node = null) { const isEdit = !!node; const title = isEdit ? '编辑节点' : '添加节点'; @@ -356,7 +227,7 @@ class KnowledgeGraphApp { this.showModal(formHtml); - // 绑定表单提交事件 + // 表单提交 document.getElementById('node-form').addEventListener('submit', (e) => { e.preventDefault(); this.saveNode(node); @@ -367,45 +238,7 @@ class KnowledgeGraphApp { }); } - async saveNode(existingNode) { - try { - const name = document.getElementById('node-name').value; - const labels = document.getElementById('node-labels').value.split(',').map(l => l.trim()).filter(l => l); - const propertiesText = document.getElementById('node-properties').value || '{}'; - - // 安全地解析JSON - let properties; - try { - properties = JSON.parse(propertiesText); - } catch (e) { - throw new Error('属性JSON格式不正确'); - } - - properties.name = name; // 确保名称属性 - - let result; - if (existingNode) { - console.log("更新节点:", existingNode.id, properties); - result = await this.api.updateNode(existingNode.id, properties); - } else { - console.log("创建节点:", properties, labels); - result = await this.api.createNode({ properties, labels }); - } - - // 检查API响应是否成功 - if (result && result.success) { - this.showSuccessMessage(existingNode ? '节点更新成功!' : '节点创建成功!'); - this.hideModal(); - this.loadData(); // 刷新数据 - } else { - throw new Error(result?.message || '操作失败,请检查服务器响应'); - } - } catch (error) { - console.error('保存节点失败:', error); - this.showErrorMessage('保存节点失败: ' + error.message); - } - } - + // 编辑时传入关系对象,添加时传入null showEdgeForm(edge = null) { const isEdit = !!edge; const title = isEdit ? '编辑关系' : '添加关系'; @@ -438,7 +271,7 @@ class KnowledgeGraphApp { this.showModal(formHtml); - // 绑定表单提交事件 + // 表单提交事件 document.getElementById('edge-form').addEventListener('submit', (e) => { e.preventDefault(); this.saveEdge(edge); @@ -449,89 +282,7 @@ class KnowledgeGraphApp { }); } - // 显示成功消息 - showSuccessMessage(message) { - this.showMessage(message, 'success'); - } - - // 显示错误消息 - showErrorMessage(message) { - this.showMessage(message, 'error'); - } - - // 显示消息(支持成功和错误类型) - showMessage(message, type = 'info') { - // 移除已存在的消息框 - const existingMessage = document.getElementById('operation-message'); - if (existingMessage) { - existingMessage.remove(); - } - - // 创建消息元素 - const messageDiv = document.createElement('div'); - messageDiv.id = 'operation-message'; - messageDiv.className = `message ${type}`; - messageDiv.innerHTML = ` - ${message} - - `; - - // 添加到页面 - document.body.appendChild(messageDiv); - - // 添加关闭按钮事件 - document.getElementById('close-message-btn').addEventListener('click', () => { - messageDiv.remove(); - }); - - // 3秒后自动消失(仅对成功消息) - if (type === 'success') { - setTimeout(() => { - if (messageDiv.parentNode) { - messageDiv.remove(); - } - }, 3000); - } - } - - async saveEdge(existingEdge) { - try { - const startNodeId = document.getElementById('edge-start').value; - const endNodeId = document.getElementById('edge-end').value; - const type = document.getElementById('edge-type').value; - const propertiesText = document.getElementById('edge-properties').value || '{}'; - - // 安全地解析JSON - let properties; - try { - properties = JSON.parse(propertiesText); - } catch (e) { - throw new Error('属性JSON格式不正确'); - } - - let result; - if (existingEdge) { - console.log("更新关系:", existingEdge.id, properties); - result = await this.api.updateEdge(existingEdge.id, properties); - } else { - console.log("创建关系:", startNodeId, endNodeId, type, properties); - result = await this.api.createEdge({ startNodeId, endNodeId, type, properties }); - } - - // 检查API响应是否成功 - if (result && result.success) { - this.showSuccessMessage(existingEdge ? '关系更新成功!' : '关系创建成功!'); - this.hideModal(); - this.loadData(); // 刷新数据 - } else { - throw new Error(result?.message || '操作失败,请检查服务器响应'); - } - } catch (error) { - console.error('保存关系失败:', error); - this.showErrorMessage('保存关系失败: ' + error.message); - } - } - + // 显示节点详情 async showNodeDetail(nodeId) { if (!this.isValidId(nodeId)) { alert('无效的节点ID'); @@ -541,7 +292,6 @@ class KnowledgeGraphApp { try { console.log("显示节点详情,ID:", nodeId); - // 首先检查节点是否在当前数据中 const nodeInCurrentData = this.currentData.nodes.find(n => n.id === nodeId); if (!nodeInCurrentData) { console.warn("节点不在当前数据中,尝试从API获取"); @@ -598,6 +348,7 @@ class KnowledgeGraphApp { } } + // 显示关系详情 async showEdgeDetail(edgeId) { if (!this.isValidId(edgeId)) { alert('无效的关系ID'); @@ -606,7 +357,15 @@ class KnowledgeGraphApp { try { console.log("显示关系详情,ID:", edgeId); + console.log("当前数据中的所有关系ID:", this.currentData.edges.map(e => e.id)); + // 检查关系是否在当前数据中 + const edgeInCurrentData = this.currentData.edges.find(e => e.id === edgeId); + if (edgeInCurrentData) { + console.log("关系在当前数据中找到:", edgeInCurrentData); + } else { + console.warn("关系不在当前数据中"); + } const response = await this.api.getEdge(edgeId); if (!response.success) { @@ -654,19 +413,20 @@ class KnowledgeGraphApp { }); } catch (error) { console.error('获取关系详情失败:', error); - alert('获取关系详情失败: ' + error.message); - - // 显示错误详情 + // 显示更详细的错误信息 const errorHtml = `

错误

获取关系详情失败

错误信息: ${error.message}

关系ID: ${edgeId}

+

当前数据中的关系ID列表:

+
${JSON.stringify(this.currentData.edges.map(e => ({ id: e.id, type: e.type })), null, 2)}
`; this.showModal(errorHtml); } } + // 删除节点 async deleteNode(nodeId) { try { await this.api.deleteNode(nodeId); @@ -678,6 +438,7 @@ class KnowledgeGraphApp { } } + // 删除关系 async deleteEdge(edgeId) { try { await this.api.deleteEdge(edgeId); @@ -689,6 +450,28 @@ class KnowledgeGraphApp { } } + // 搜索 + async handleSearch() { + const query = document.getElementById('search-input').value.trim(); + if (!query) return; + + try { + const response = await this.api.searchNodes('name', query); + + if (response.data.length > 0) { + this.graphRenderer.highlightNodes(response.data.map(node => node.id)); + } else { + alert('未找到匹配的节点'); + } + } catch (error) { + console.error('搜索失败:', error); + alert('搜索失败'); + } + } + + /* + * 模态框 + */ showModal(content) { document.getElementById('modal-body').innerHTML = content; document.getElementById('modal').style.display = 'block'; @@ -697,12 +480,226 @@ class KnowledgeGraphApp { hideModal() { document.getElementById('modal').style.display = 'none'; } + + debugInfo() { + console.log("=== 调试信息开始 ==="); + console.log("当前配置:", this.config); + console.log("最大节点数:", this.config.maxNodes); + console.log("最大边数:", this.config.maxEdges); + + console.log("API基础URL:", this.api.baseUrl); + + console.log("测试API连接..."); + + fetch(`${this.api.baseUrl}/nodes?limit=1`) + .then(response => { + console.log("API节点响应状态:", response.status, response.statusText); + return response.json(); + }) + .then(data => { + console.log("API节点测试成功:", data); + }) + .catch(error => { + console.error("API节点测试失败:", error); + }); + + fetch(`${this.api.baseUrl}/edges?limit=1`) + .then(response => { + console.log("API边响应状态:", response.status, response.statusText); + return response.json(); + }) + .then(data => { + console.log("API边测试成功:", data); + }) + .catch(error => { + console.error("API边测试失败:", error); + }); + + console.log("图谱容器:", this.graphRenderer.containerId); + console.log("图谱实例:", this.graphRenderer.graph ? "已初始化" : "未初始化"); + + const buttons = ['refresh-btn', 'add-node-btn', 'add-edge-btn', 'debug-btn', 'search-btn', 'debug-apply-limit', 'debug-apply-edge-limit']; + buttons.forEach(btnId => { + const btn = document.getElementById(btnId); + console.log(`按钮 ${btnId}:`, btn ? "存在" : "不存在"); + }); + + console.log("=== 调试信息结束 ==="); + + alert("调试信息已输出到控制台,请按F12查看"); + } + + isValidId(id) { + return id && typeof id === 'string' && id.length > 0; + } + + renderSidebarLists() { + const nodesContainer = document.getElementById('nodes-container'); + const edgesContainer = document.getElementById('edges-container'); + + nodesContainer.innerHTML = ''; + edgesContainer.innerHTML = ''; + + const statsHtml = ` +
+ 统计信息:
+ 节点: ${this.currentData.nodes.length}/${this.config.maxNodes}
+ 关系: ${this.currentData.edges.length}/${this.config.maxEdges} +
+ `; + + nodesContainer.innerHTML = statsHtml; + + this.currentData.nodes.forEach(node => { + const li = document.createElement('li'); + const displayName = node.properties && node.properties.name + ? node.properties.name + : `节点 ${node.id ? node.id.substring(0, 8) : '未知'}`; + + li.textContent = displayName; + li.dataset.id = node.id; + li.addEventListener('click', () => { + console.log("点击节点,ID:", node.id); + this.showNodeDetail(node.id); + }); + nodesContainer.appendChild(li); + }); + + this.currentData.edges.forEach(edge => { + const li = document.createElement('li'); + const startName = edge.startNode && edge.startNode.properties && edge.startNode.properties.name + ? edge.startNode.properties.name + : (edge.startNodeId || '未知'); + const endName = edge.endNode && edge.endNode.properties && edge.endNode.properties.name + ? edge.endNode.properties.name + : (edge.endNodeId || '未知'); + + li.textContent = `${edge.type || '关系'}: ${startName} → ${endName}`; + li.dataset.id = edge.id; + li.addEventListener('click', () => { + console.log("点击关系,ID:", edge.id); + this.showEdgeDetail(edge.id); + }); + edgesContainer.appendChild(li); + }); + } + + async saveNode(existingNode) { + try { + const name = document.getElementById('node-name').value; + const labels = document.getElementById('node-labels').value.split(',').map(l => l.trim()).filter(l => l); + const propertiesText = document.getElementById('node-properties').value || '{}'; + + let properties; + try { + properties = JSON.parse(propertiesText); + } catch (e) { + throw new Error('属性JSON格式不正确'); + } + + properties.name = name; // 确保名称属性 + + let result; + if (existingNode) { + console.log("更新节点:", existingNode.id, properties); + result = await this.api.updateNode(existingNode.id, properties); + } else { + console.log("创建节点:", properties, labels); + result = await this.api.createNode({ properties, labels }); + } + + // 检查API响应是否成功 + if (result && result.success) { + this.showSuccessMessage(existingNode ? '节点更新成功!' : '节点创建成功!'); + this.hideModal(); + this.loadData(); // 刷新数据 + } else { + throw new Error(result?.message || '操作失败,请检查服务器响应'); + } + } catch (error) { + console.error('保存节点失败:', error); + this.showErrorMessage('保存节点失败: ' + error.message); + } + } + + showSuccessMessage(message) { + this.showMessage(message, 'success'); + } + + showErrorMessage(message) { + this.showMessage(message, 'error'); + } + + showMessage(message, type = 'info') { + const existingMessage = document.getElementById('operation-message'); + if (existingMessage) { + existingMessage.remove(); + } + + const messageDiv = document.createElement('div'); + messageDiv.id = 'operation-message'; + messageDiv.className = `message ${type}`; + messageDiv.innerHTML = ` + ${message} + + `; + + document.body.appendChild(messageDiv); + + document.getElementById('close-message-btn').addEventListener('click', () => { + messageDiv.remove(); + }); + + if (type === 'success') { + setTimeout(() => { + if (messageDiv.parentNode) { + messageDiv.remove(); + } + }, 3000); + } + } + + async saveEdge(existingEdge) { + try { + const startNodeId = document.getElementById('edge-start').value; + const endNodeId = document.getElementById('edge-end').value; + const type = document.getElementById('edge-type').value; + const propertiesText = document.getElementById('edge-properties').value || '{}'; + + let properties; + try { + properties = JSON.parse(propertiesText); + } catch (e) { + throw new Error('属性JSON格式不正确'); + } + + let result; + if (existingEdge) { + console.log("更新关系:", existingEdge.id, properties); + result = await this.api.updateEdge(existingEdge.id, properties); + } else { + console.log("创建关系:", startNodeId, endNodeId, type, properties); + result = await this.api.createEdge({ startNodeId, endNodeId, type, properties }); + } + + if (result && result.success) { + this.showSuccessMessage(existingEdge ? '关系更新成功!' : '关系创建成功!'); + this.hideModal(); + this.loadData(); + } else { + throw new Error(result?.message || '操作失败,请检查服务器响应'); + } + } catch (error) { + console.error('保存关系失败:', error); + this.showErrorMessage('保存关系失败: ' + error.message); + } + } } // 初始化应用 document.addEventListener('DOMContentLoaded', () => { const app = new KnowledgeGraphApp(); - window.app = app; // 设置为全局变量 + window.app = app; }); // 全局错误处理 diff --git a/code/frontend/graph-renderer.js b/code/frontend/graph-renderer.js index db1e2c6..dbf3ecd 100644 --- a/code/frontend/graph-renderer.js +++ b/code/frontend/graph-renderer.js @@ -17,6 +17,7 @@ class GraphRenderer { const height = container.clientHeight || 600; this.graph = new G6.Graph({ + // G6 画布配置 container: this.containerId, width, height, @@ -56,7 +57,6 @@ class GraphRenderer { } }); - // 调整画布大小 window.addEventListener('resize', () => { if (!this.graph || this.graph.get('destroyed')) return; const container = document.getElementById(this.containerId); @@ -66,10 +66,10 @@ class GraphRenderer { }); } + //将数据转换为G6格式并渲染 render(data) { if (!this.graph) return; - // 确保节点有正确的ID和标签 const nodes = data.nodes.map(node => ({ id: node.id, label: node.properties && node.properties.name @@ -79,7 +79,6 @@ class GraphRenderer { labels: node.labels || [] })); - // 确保边有正确的源和目标 const edges = data.edges.map(edge => ({ id: edge.id, source: edge.startNode ? edge.startNode.id : edge.startNodeId, @@ -99,16 +98,16 @@ class GraphRenderer { this.graph.render(); this.graph.fitView(); - // 绑定点击事件 this.bindGraphEvents(); } + // ………………… + + // 事件绑定 bindGraphEvents() { - // 移除旧的事件监听器 this.graph.off('node:click'); this.graph.off('edge:click'); - // 绑定新的事件 this.graph.on('node:click', (evt) => { const node = evt.item; const nodeId = node.get('model').id; @@ -118,30 +117,30 @@ class GraphRenderer { this.app.showNodeDetail(nodeId); } else { console.error('应用实例未找到或showNodeDetail方法不存在'); - // 备用方案:使用全局事件 document.dispatchEvent(new CustomEvent('nodeClick', { detail: nodeId })); } }); this.graph.on('edge:click', (evt) => { const edge = evt.item; - const edgeId = edge.get('model').id; - console.log('边被点击:', edgeId); + const edgeModel = edge.get('model'); + const edgeId = edgeModel.id; + console.log('边被点击 - 模型数据:', edgeModel); + console.log('边被点击 - ID:', edgeId); if (this.app && this.app.showEdgeDetail) { this.app.showEdgeDetail(edgeId); } else { console.error('应用实例未找到或showEdgeDetail方法不存在'); - // 备用方案:使用全局事件 document.dispatchEvent(new CustomEvent('edgeClick', { detail: edgeId })); } }); } + // 高亮节点 highlightNodes(nodeIds) { if (!this.graph) return; - // 先将所有节点恢复默认样式 this.graph.getNodes().forEach(node => { this.graph.updateItem(node, { style: { @@ -151,7 +150,6 @@ class GraphRenderer { }); }); - // 高亮选中的节点 nodeIds.forEach(nodeId => { const node = this.graph.findById(nodeId); if (node) { @@ -164,24 +162,18 @@ class GraphRenderer { } }); - // 聚焦到选中的节点 this.graph.focusItem(nodeIds[0]); } } -// // 监听节点和边点击事件 // document.addEventListener('nodeClick', (e) => { -// // 在实际应用中,这里可以调用应用实例的方法 // console.log('节点被点击:', e.detail); -// // 例如: app.showNodeDetail(e.detail); // }); // document.addEventListener('edgeClick', (e) => { // console.log('边被点击:', e.detail); -// // 例如: app.showEdgeDetail(e.detail); // }); -// 备用方案:通过全局事件处理 document.addEventListener('nodeClick', (e) => { console.log('通过全局事件处理节点点击:', e.detail); if (window.app && window.app.showNodeDetail) { diff --git a/code/frontend/index.html b/code/frontend/index.html index 6c3aa99..0e957fe 100644 --- a/code/frontend/index.html +++ b/code/frontend/index.html @@ -6,7 +6,6 @@ 知识图谱管理系统 - @@ -14,20 +13,17 @@

知识图谱管理系统

-
-
节点:
-
边: @@ -61,7 +57,6 @@

关系列表

-