Browse Source

Merge branch 'beta' into English

lsc 19 hours ago
parent
commit
b4facea06d

+ 4 - 0
dist/index.html

@@ -32,7 +32,11 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
+<<<<<<< HEAD
     }</style><link href=./static/css/app.637debc6fc2a76fe1c9822b8d68e2041.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.8c9ac76522a5e4bf9301.js></script><script type=text/javascript src=./static/js/app.1985311babf923a86a17.js></script></body></html><script>function stopSafari() {
+=======
+    }</style><link href=./static/css/app.fac85a2e5041655b3e07c12771e3aac1.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.4dce3a5441c732d84c44.js></script><script type=text/javascript src=./static/js/app.0868f46c01c051e64236.js></script></body></html><script>function stopSafari() {
+>>>>>>> beta
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.637debc6fc2a76fe1c9822b8d68e2041.css.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.fac85a2e5041655b3e07c12771e3aac1.css


File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.fac85a2e5041655b3e07c12771e3aac1.css.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.0868f46c01c051e64236.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.0868f46c01c051e64236.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/vendor.4dce3a5441c732d84c44.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/vendor.4dce3a5441c732d84c44.js.map


+ 46 - 42
src/components/pages/knowledge/components/checkDialog.vue

@@ -361,62 +361,66 @@ export default {
         lines: lines
       };
     },
-    transformData2(data) {
-        const nodes2 = [];
-        const lines2 = [];
-        // 构建节点
-        const nodeMap2 = new Map();
-
-        data.results.forEach(item => {
-            const subject = item.subject;
-            const object = item.object;
-            const subjectId = item.subjectId;
-            const objectId = item.objectId;
-
-            // 构建节点
-            const addNode = (id, label) => {
-                if (!nodeMap2.has(id)) {
-                    nodeMap2.set(id, { id, label });
-                    nodes2.push(nodeMap2.get(id));
-                }
-            };
-
-            addNode(subjectId, subject);
-            addNode(objectId, object);
-
-            // 添加关系线
-            lines2.push({
-                source: item.subjectId,
-                target: item.objectId,
-                predicate: item.predicate
-            });
-        });
+    transformData2(data, Entities) {
+      // 优化:先将Entities转为Map,提升查找效率
+      const entityMap = new Map();
+      (Entities.results || []).forEach(ent => {
+        entityMap.set(ent.id, {category: ent.category || '', description: ent.description || ''});
+      });
 
-        console.log({
-            nodes: nodes2,
-            edges: lines2
+      const nodes2 = [];
+      const lines2 = [];
+      const nodeMap2 = new Map();
+
+      // 辅助函数:添加节点
+      const addNode = (id, label) => {
+        if (!nodeMap2.has(id)) {
+          const Entitie = entityMap.get(id) || '';
+          const node = { id, label, category:Entitie.category, description: Entitie.description};
+          nodeMap2.set(id, node);
+          nodes2.push(node);
+        }
+      };
+
+      (data.results || []).forEach(item => {
+        const { subject, object, subjectId, objectId, predicate } = item;
+        addNode(subjectId, subject);
+        addNode(objectId, object);
+
+        // 添加关系线
+        lines2.push({
+          source: subjectId,
+          target: objectId,
+          predicate
         });
+      });
+
+      // 输出调试信息
+      console.log({
+        nodes: nodes2,
+        edges: lines2
+      });
 
-        return {
-            nodes: nodes2,
-            edges: lines2
-        };
+      return {
+        nodes: nodes2,
+        edges: lines2
+      };
     },
     setGrapJson() {
       this.graphLoading = true;
       let params = {
         documentId: this.did,
-        limit: 100,
+        limit: 1000,
       };
       // 获取切片
       this.ajax
-        .post(this.$store.state.fileApi + "getRelationships", [params])
+        .post(this.$store.state.fileApi + "getEntitiesAndRelationships", [params])
         .then(res => {
           this.graphLoading = false;
           console.log(res.data);
-          if (res.data.result.results.length) {
-            console.log(this.transformData2(res.data.result));
-            this.graphJson = this.transformData2(res.data.result)
+          if (res.data.Relationships.results.length) {
+            console.log(this.transformData2(res.data.Relationships, res.data.Entities));
+            this.graphJson = this.transformData2(res.data.Relationships, res.data.Entities)
           }else {
             this.graphJson = {}
           }

+ 223 - 39
src/components/pages/knowledge/components/graph.vue

@@ -1,6 +1,15 @@
 <template>
-    <div style="width: 100%; max-height: 600px; overflow: auto;padding-bottom: 10px;" class="content">
-        <div style="height:600px;position: relative;" ref="container"></div>
+    <div>
+        <div class="graph-toolbar">
+            <button class="graph-btn" @click="zoomIn">放大</button>
+            <button class="graph-btn" @click="zoomOut">缩小</button>
+            <button class="graph-btn" @click="resetZoom">1:1</button>
+            <input v-model="searchText" @keyup.enter="searchNode" placeholder="搜索节点" class="graph-input" />
+            <button class="graph-btn graph-btn-primary" @click="searchNode">搜索</button>
+        </div>
+        <div style="width: 100%; max-height: 600px; overflow: auto;padding-bottom: 10px;" class="content">
+            <div style="height:600px;position: relative;" ref="container"></div>
+        </div>
     </div>
 </template>
 
@@ -12,6 +21,7 @@ export default {
     data() {
         return {
             graph: null,
+            searchText: '',
         }
     },
     mounted() {
@@ -42,6 +52,48 @@ export default {
         }
     },
     methods: {
+        zoomIn() {
+            if (this.graph) {
+                const curZoom = this.graph.getZoom();
+                this.graph.zoomTo(curZoom * 1.2, { x: 0, y: 0 });
+            }
+        },
+        zoomOut() {
+            if (this.graph) {
+                const curZoom = this.graph.getZoom();
+                this.graph.zoomTo(curZoom / 1.2, { x: 0, y: 0 });
+            }
+        },
+        resetZoom() {
+            if (this.graph) {
+                this.graph.zoomTo(1, { x: 0, y: 0 });
+            }
+        },
+        searchNode() {
+            if (!this.graph || !this.searchText) return;
+            const nodes = this.graph.getNodes();
+            let found = false;
+            nodes.forEach(node => {
+                const model = node.getModel();
+                // 先清除所有高亮
+                this.graph.clearItemStates(node, 'searched');
+                if (
+                    (model.fullLabel && model.fullLabel.indexOf(this.searchText) !== -1) ||
+                    (model.label && model.label.indexOf(this.searchText) !== -1) ||
+                    (model.id && model.id.indexOf(this.searchText) !== -1)
+                ) {
+                    this.graph.focusItem(node, true, {
+                        easing: 'easeCubic',
+                        duration: 600
+                    });
+                    this.graph.setItemState(node, 'searched', true);
+                    found = true;
+                }
+            });
+            if (!found) {
+                this.$message && this.$message.warning('未找到该节点');
+            }
+        },
         showSeeksGraph(data) {
             if (this.graph) {
                 this.graph.destroy();
@@ -56,6 +108,7 @@ export default {
                 width: container.scrollWidth,
                 height: container.scrollHeight || 600,
                 fitView: true,
+                background: { color: '#f7faff' },
                 modes: {
                     default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
                 },
@@ -69,28 +122,101 @@ export default {
                 },
                 defaultNode: {
                     size: 40,
-                    style: {
-                        fill: 'rgba(24, 144, 255, 0.2)', // 浅蓝半透明
-                        stroke: '#1890ff', // 实色边框
-                        lineWidth: 2 // 边框宽度
-                    },
                     labelCfg: {
-                        position: 'bottom', // 标签显示在节点下方
+                        position: 'bottom',
                         style: {
                             fill: '#000',
-                            fontSize: 12
+                            fontSize: 14
+                        }
+                    },
+                    stateStyles: {
+                        highlight: {
+                            opacity: 1,
+                        },
+                        inactive: {
+                            opacity: 0.15,
+                        },
+                        searched: {
+                            stroke: '#1890ff',
+                            lineWidth: 2,
+                            shadowColor: '#1890ff',
+                            opacity: 1,
+                            fill: '#f7faff'
                         }
                     }
                 },
                 defaultEdge: {
-                    type: 'cubic', // 贝塞尔曲线
+                    type: 'cubic',
                     style: {
                         endArrow: true,
                     },
-                     labelCfg: {
+                    labelCfg: {
                         autoRotate: true,
+                    },
+                    stateStyles: {
+                        highlight: {
+                            opacity: 1,
+                        },
+                        inactive: {
+                            opacity: 0.08,
+                        }
                     }
-                }
+                },
+                plugins: [
+                    new G6.Tooltip({
+                        offsetX: 50,
+                        offsetY: -500,
+                        itemTypes: ['node'],
+                        getContent: (e) => {
+                            const outDiv = document.createElement('div');
+                            outDiv.style.maxWidth = '320px';
+                            outDiv.style.wordBreak = 'break-all';
+                            const model = e.item.getModel();
+                            // 显示完整名称,不受字数限制
+                            const fullLabel = model.fullLabel || model.label || model.id || '未命名节点';
+                            outDiv.innerHTML = `
+                            <div>
+                                <div style="margin-bottom: 5px;font-weight: bold;">${fullLabel}</div>
+                                <span style="color:#666;font-size:12px;">${model.description || '暂无描述'}</span>
+                            </div>`;
+                            return outDiv;
+                        }
+                    })
+                ]
+            });
+
+            // 层级高亮
+            this.graph.on('node:mouseenter', (e) => {
+                const node = e.item;
+                const graph = this.graph;
+                // 先全部虚化
+                graph.getNodes().forEach(n => graph.setItemState(n, 'inactive', true));
+                graph.getEdges().forEach(edge => graph.setItemState(edge, 'inactive', true));
+                // 当前节点高亮
+                graph.setItemState(node, 'inactive', false);
+                graph.setItemState(node, 'highlight', true);
+                // 直接相连的边和节点高亮
+                node.getEdges().forEach(edge => {
+                    graph.setItemState(edge, 'inactive', false);
+                    graph.setItemState(edge, 'highlight', true);
+                    const source = edge.getSource();
+                    const target = edge.getTarget();
+                    [source, target].forEach(n => {
+                        graph.setItemState(n, 'inactive', false);
+                        graph.setItemState(n, 'highlight', true);
+                    });
+                });
+            });
+            
+            this.graph.on('node:mouseleave', (e) => {
+                const graph = this.graph;
+                // 恢复所有状态
+                graph.getNodes().forEach(n => {
+                    graph.clearItemStates(n);
+                });
+                graph.getEdges().forEach(edge => {
+                    graph.clearItemStates(edge);
+                });
             });
 
 
@@ -103,41 +229,58 @@ export default {
                 if (edge.target) linkCount[edge.target] = (linkCount[edge.target] || 0) + 1;
             });
 
-            // 六档蓝色系(更有区分度)
-            const strokeColors = [
-                '#b3e5fc', // 0 浅蓝青
-                '#4fc3f7', // 1 天蓝
-                '#0288d1', // 2 深天蓝
-                '#1976d2', // 3 标准蓝
-                '#1565c0', // 4 深蓝
-                '#0d47a1', // 5 靛蓝
-                '#002171'  // 6+ 极深蓝
-            ];
-            const fillColors = [
-                'rgba(179,229,252,0.2)',
-                'rgba(79,195,247,0.2)',
-                'rgba(2,136,209,0.2)',
-                'rgba(25,118,210,0.2)',
-                'rgba(21,101,192,0.2)',
-                'rgba(13,71,161,0.2)',
-                'rgba(0,33,113,0.2)'
-            ];
+            // 随机颜色函数
+            const categoryColors = new Map();
+            const getRandomColor = () => {
+                const letters = '0123456789ABCDEF';
+                let color = '#';
+                for (let i = 0; i < 6; i++) {
+                    color += letters[Math.floor(Math.random() * 16)];
+                }
+                return color;
+            };
+
+            const getCategoryColor = (category) => {
+                if (!category) return '#1890ff'; // 默认蓝色
+                if (!categoryColors.has(category)) {
+                    categoryColors.set(category, getRandomColor());
+                }
+                return categoryColors.get(category);
+            };
+
+            // 将十六进制颜色转换为rgba格式
+            const hexToRgba = (hex, alpha = 0.2) => {
+                const r = parseInt(hex.slice(1, 3), 16);
+                const g = parseInt(hex.slice(3, 5), 16);
+                const b = parseInt(hex.slice(5, 7), 16);
+                return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+            };
 
             const graphData = {
                 nodes: data.nodes.map(node => {
                     const count = linkCount[node.id] || 0;
-                    const cappedCount = Math.min(count, 6);
                     const size = 20 + count * 10; // 不封顶
-                    const stroke = strokeColors[cappedCount];
-                    const fill = fillColors[cappedCount];
+                    const categoryColor = getCategoryColor(node.category);
+                    const fill = hexToRgba(categoryColor, 0.2);
+                    console.log(categoryColor);
+                    
+                    // 处理标签文本截断
+                    const fullLabel = node.label || node.id || '';
+                    const maxLength = 8; // 最大显示字数
+                    const displayLabel = fullLabel.length > maxLength 
+                        ? fullLabel.substring(0, maxLength) + '...' 
+                        : fullLabel;
+                    
                     // 保留 comboId 字段
                     const nodeData = {
                         id: node.id,
-                        label: node.label,
+                        label: displayLabel, // 使用截断后的标签
+                        fullLabel: fullLabel, // 保存完整标签用于tooltip
+                        description: node.description,
                         size,
                         style: {
                             fill,
-                            stroke,
+                            stroke: categoryColor,
                             lineWidth: 2
                         }
                     };
@@ -166,11 +309,11 @@ export default {
             this.graph.render();
 
             this.graph.on('node:mouseenter', (e) => {
-                this.graph.setItemState(e.item, 'hover', true);
+                this.graph.setItemState(e.item, 'focus', true);
             });
 
             this.graph.on('node:mouseleave', (e) => {
-                this.graph.setItemState(e.item, 'hover', false);
+                this.graph.setItemState(e.item, 'focus', false);
             });
         },
         handleResize() {
@@ -186,4 +329,45 @@ export default {
 
 </script>
 
-<style></style>
+<style scoped>
+.graph-toolbar {
+    display: flex;
+    align-items: center;
+    margin-bottom: 10px;
+    gap: 10px;
+}
+.graph-btn {
+    background: #fff;
+    border: 1px solid #3370ff;
+    color: #3370ff;
+    border-radius: 4px;
+    width: 60px;
+    height: 30px;
+    cursor: pointer;
+    font-size: 14px;
+    transition: background 0.2s, color 0.2s;
+    outline: none;
+}
+.graph-btn:hover {
+    background: #3370ff;
+    color: #fff;
+}
+.graph-btn-primary {
+    background: #3370ff;
+    border-color: #3370ff;
+    color: #fff;
+}
+.graph-btn-primary:hover {
+    background: #3370ff;
+    border-color: #3370ff;
+    color: #fff;
+}
+.graph-input {
+    padding: 0 10px;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    font-size: 14px;
+    height: 30px;
+    outline: none;
+}
+</style>

Some files were not shown because too many files changed in this diff