Browse Source

迁移代码

yangdr 3 weeks ago
parent
commit
c800621850

+ 7 - 0
components.d.ts

@@ -45,6 +45,7 @@ declare module 'vue' {
     ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
+    ElProgress: typeof import('element-plus/es')['ElProgress']
     ElRadio: typeof import('element-plus/es')['ElRadio']
     ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@@ -77,11 +78,17 @@ declare module 'vue' {
     LayoutHeader: typeof import('./src/components/LayoutHeader.vue')['default']
     MarkdownViewer: typeof import('./src/components/FileViewer/MarkdownViewer.vue')['default']
     OtherTerminology: typeof import('./src/components/KGBuilderTM/OtherTerminology.vue')['default']
+    PanelGraphCategoryMgr: typeof import('./src/components/PanelGraphCategoryMgr.vue')['default']
+    PanelGraphCheck: typeof import('./src/components/PanelGraphCheck.vue')['default']
+    PanelGraphNodesEdgesMgr: typeof import('./src/components/PanelGraphNodesEdgesMgr.vue')['default']
     PdfViewer: typeof import('./src/components/FileViewer/PdfViewer.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     SideMenu: typeof import('./src/components/SideMenu.vue')['default']
     SurgicalOperationTerminology: typeof import('./src/components/KGBuilderTM/SurgicalOperationTerminology.vue')['default']
+    TableContradictions: typeof import('./src/components/TableContradictions.vue')['default']
+    TableIndividualNode: typeof import('./src/components/TableIndividualNode.vue')['default']
+    TableRelation: typeof import('./src/components/TableRelation.vue')['default']
     TestingTerminology: typeof import('./src/components/KGBuilderTM/TestingTerminology.vue')['default']
     TextViewer: typeof import('./src/components/FileViewer/TextViewer.vue')['default']
     TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']

+ 266 - 0
src/components/PanelGraphCategoryMgr.vue

@@ -0,0 +1,266 @@
+<template>
+  <GraphSchemaDialog ref="graphScehmaDialog" @std-updated="handleStdSchemaUpdated"/>
+
+
+  <el-row>
+    
+  <div style=" margin-bottom: 20px; ">请为图谱中的类型进行标准化</div>
+  </el-row>
+<el-row>
+    <el-col :span="18">
+
+      <div style="display: flex; flex-direction: row; margin-bottom: 20px; ">
+        
+        <el-select style="width: 120px;margin-right: 10px;" v-model="formData.category" placeholder="请选择分类" @change="handleSearch">
+          <el-option label="实体类型" value="node"></el-option>
+          <el-option label="关系类型" value="edge"></el-option>
+        </el-select>        
+        <el-select style="width: 220px;margin-right: 10px;" v-model="formData.selectedStdSchemaId" placeholder="请选择图谱标准">
+          <el-option
+            v-for="item in stdSchemaData"
+            :key="item.name"
+            :label="item.name"
+            :value="item.id">
+          </el-option>
+        </el-select>
+        <el-button type="primary" @click="handleMapStdSchema" >自动标准匹配</el-button>
+<!--         
+        <el-button type="primary" @click="handleSave">更新图谱数据</el-button> -->
+        <el-button type="primary" @click="handleShowStdSchema">编辑图谱标准</el-button>
+
+ </div>
+
+ <el-table v-loading="dataLoading" :data="categoryData"  style="width: 500px; border:1px solid #EFEFEF;">
+    <el-table-column prop="name" label="数据类型" width="150" >
+      <template #default="scope">
+        <span>{{ scope.row.name }}</span>&nbsp;<a href="#" @click="handleViewDataSample(scope.row)">样本</a>
+      </template>
+    </el-table-column>
+
+    <el-table-column prop="std_name" label="标准类型" width="150">
+      <template #default="scope">
+        
+        <el-select v-if="formData.category=='node'" v-model="scope.row.std_name" placeholder="请选择">
+          <el-option
+            v-for="item in selectedStdSchema?.entities"
+            :key="item.name"
+            :label="item.name"
+            :value="item.name">
+          </el-option>
+        </el-select>
+        
+        <el-select v-if="formData.category=='edge'" v-model="scope.row.std_name" placeholder="请选择">
+          <el-option
+            v-for="item in selectedStdSchema?.relations"
+            :key="item.name"
+            :label="item.name"
+            :value="item.name">
+          </el-option>
+        </el-select>
+      </template>
+    </el-table-column>
+    <el-table-column prop="name" label="" width="100" >
+      <template #default="scope">
+        <el-button type="success" plain @click="handleUpdateCategory(scope.row)">更新数据</el-button>
+      </template>
+    </el-table-column>
+
+  </el-table>
+
+
+    </el-col>
+</el-row>
+</template>
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import GraphSchemaDialog from '@/dialogs/GraphSchemaDialog.vue'
+import {getGraphNodeSchema, 
+  getGraphEdgeSchema, 
+  getGraphStdSchemas, 
+  searchStdSchema, 
+  updateGraphNodeSchema,updateGraphEdgeSchema,
+  searchNodes,searchEdges,
+  updateStdSchemaContent} from '@/api/GraphApi'
+
+import { ElMessageBox, ElNotification } from 'element-plus'
+import { type ViewSchemaData, type SchemaData, type ViewCategoryData} from '@/types/dataset'
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0}
+  
+})
+const formData = ref({
+  category: 'node',
+  updateStdSchema: false,
+  std_name_tmp: '',
+  new_std_name : '',
+  selectedStdSchemaId: 0,
+})
+
+const graphScehmaDialog = ref()
+const dataLoading = ref(false)
+const categoryData = ref<ViewCategoryData[]>([])
+const stdSchemaData = ref<ViewSchemaData[]>([])
+const newStdNameDialogVisible = ref(false)
+const selectedStdSchema = ref<SchemaData>()
+function handleUpdateCategory(data:any){
+  if (data.name == data.std_name){
+    ElNotification({
+      title: 'Error',
+      message: '数据类型和标准类型相同',
+      type: 'error',
+    })
+    return
+  }
+  if (data.std_name == ''){
+    ElNotification({
+      title: 'Error',
+      message: '请选择标准类型',
+      type: 'error',
+    })
+    return
+  }
+  ElMessageBox.confirm(`是否更新数据类型[${data.name}]为标准类型[${data.std_name}]?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning', 
+  }).then(() => {    
+    if (data.type == 'node'){
+      updateGraphNodeSchema({graph_id: props.graphId, category: [data.name], category_new: [data.std_name]}).then(response => {
+        ElNotification({
+          title: 'Success',
+          message: '更新成功',
+          type: 'success',
+        })
+        handleSearch()
+      })     
+    }
+    if (data.type== 'edge'){
+      updateGraphEdgeSchema({graph_id: props.graphId, category: [data.name], category_new: [data.std_name]}).then(response => {
+        ElNotification({
+          title: 'Success',
+          message: '更新成功',
+          type:'success',
+        })
+        handleSearch()
+      })
+    }
+  })
+}
+function handleViewDataSample(data:any){
+  if (data.type == 'node'){    
+    searchNodes("", props.graphId, data.name).then( response => {
+      var nodes = response.records;
+      var names = []
+      for (var i = 0; i < nodes.length; i++) {
+        names.push( nodes[i]['name'] )
+        if (i > 10) break;
+      }
+      names.join(", ")
+      ElMessageBox.alert(names.join(","), '样本', {
+        confirmButtonText: '确定',
+
+      })
+    })
+  }
+  if (data.type == 'edge'){
+
+    searchEdges("", props.graphId, data.name).then( response => {
+      var edges = response.records;
+      var names = []
+      for (var i = 0; i < edges.length; i++) {
+        names.push(edges[i]['src_node_name'] +"-"+ edges[i]['name'] + "->"+ edges[i]['dest_node_name'])
+        if (i > 10) break;
+      }
+      names.join("<br/>")
+      ElMessageBox.alert(names.join(","), '样本', {
+        confirmButtonText: '确定',
+
+      })
+    })
+  }
+}
+
+function handleStdSchemaUpdated(){
+  console.log("handleStdSchemaUpdated")
+  getGraphStdSchemas({page:1, page_size:10}).then(response => {
+    stdSchemaData.value = []; 
+    response.records.forEach((element:any) => {
+      var item:ViewSchemaData = {
+        id: element.id, name: element.name, category: element.category, content: element.content,
+        data: {
+          entities: [],
+          relations: []
+        }
+      };
+      item.data = JSON.parse(element["content"]) // 解析 JSON 字符串为 JavaScript 对象
+      stdSchemaData.value?.push(item) 
+    })
+    selectedStdSchema.value = stdSchemaData.value[0].data;
+    formData.value.selectedStdSchemaId = stdSchemaData.value[0].id;
+
+  }) 
+}
+function handleMapStdSchema(){
+  console.log("handleMapStdSchema", selectedStdSchema.value)
+  categoryData.value.forEach(element => {
+    
+    searchStdSchema({type: formData.value.category, query_word: element.name, schema_id:formData.value.selectedStdSchemaId}).then(response => {
+        if (response.records.length > 0){
+          element.std_name = response.records[0].std_name;
+        }                 
+
+        
+    })
+  })
+
+}
+function handleShowStdSchema(){
+  console.log(selectedStdSchema.value)
+  graphScehmaDialog.value.showDialog(true, selectedStdSchema.value)
+}
+function handleSearch() {
+  if (formData.value.category === 'node') {
+    dataLoading.value = true
+    getGraphNodeSchema({graph_id : props.graphId}).then(response => {
+        categoryData.value = []
+        response.records.forEach((element:any) => {
+            var item:ViewCategoryData = {name: element.name, std_name: '', type: 'node'}
+            categoryData.value.push(item)
+        });
+        dataLoading.value = false
+    })
+  }
+  if (formData.value.category === 'edge') {
+    getGraphEdgeSchema({graph_id : props.graphId}).then(response => {
+        categoryData.value = []
+        response.records.forEach((element:any) => {
+            var item:ViewCategoryData = {name: element.name, std_name: '', type: 'edge'}
+            categoryData.value.push(item)
+        });
+    })
+  }
+}
+
+function loadDefaultData() {
+  getGraphStdSchemas({page:1, page_size:10}).then(response => {
+    stdSchemaData.value = []; 
+    response.records.forEach((element:any) => {
+      var item:ViewSchemaData = {
+        id: element.id, name: element.name, category: element.category, content: element.content,
+        data: {
+          entities: [],
+          relations: []
+        }
+      };
+      element.data = JSON.parse(element["content"]) // 解析 JSON 字符串为 JavaScript 对象
+      stdSchemaData.value.push(element) 
+    })
+    selectedStdSchema.value = stdSchemaData.value[0].data;
+    formData.value.selectedStdSchemaId = stdSchemaData.value[0].id;
+    handleSearch()
+  }) 
+}
+onMounted(() => {
+  loadDefaultData() 
+})
+</script>

+ 280 - 0
src/components/PanelGraphCheck.vue

@@ -0,0 +1,280 @@
+<template>
+    <GraphNodeDialog ref="graphNodeDialog" :title="graphNodeDialogTitle" :graph-id="props.graphId"></GraphNodeDialog>
+    <el-row>
+        <el-col :span="20">
+            <el-row>
+                <div v-if="graphData.status == GraphStatus.GRAPH_STATUS_NORMAL">图谱数据还没有进行检查</div>
+                <div v-if="graphData.status == GraphStatus.GRAPH_STATUS_NEED_CHECK">图谱数据已经开始检查</div>
+                <div v-if="graphData.status == GraphStatus.GRAPH_STATUS_CHECKING">
+                    图谱数据正在进行检查……
+
+                    总计 {{ progressData.nodes_count }}个节点,{{ progressData.edges_count }}个关系,
+                    已处理 {{ progressData.processed_nodes_count }}个节点,{{ progressData.processed_edges_count }}个关系
+                </div>
+                <div v-if="graphData.status == GraphStatus.GRAPH_STATUS_CHECKED">图谱数据检查完成</div>
+
+            </el-row>
+
+            <el-row style="margin:15px;">
+                <el-form :model="searchForm" :inline="true" label-position="right">
+                    <el-form-item label="检查项目" prop="event_name" :label-width="70"
+                        style="margin-left:0px;margin-right:0px;">
+                        <el-select v-model="searchForm.event_name" placeholder="请选择任务类型" style="width:100px;">
+                            <el-option v-for="item in pageData.event_names" :key="item" :label="item"
+                                :value="item"></el-option>
+                        </el-select>
+                    </el-form-item>
+
+                    <el-form-item label="任务类型" prop="start_category" :label-width="70"
+                        style="margin-left:5px;margin-right:0px;">
+
+                    </el-form-item>
+                    <el-form-item style="justify-items: right;margin-left:10px;">
+                        <el-button type="primary" @click="handleSearch">搜索</el-button>
+                        <el-button type="primary" @click="handleSearchReset" style="margin-left:5px;">重置</el-button>
+                        <el-button @click="handleRefreshTable" style="margin-left:5px;" type="success">刷新</el-button>
+                    </el-form-item>
+                </el-form>
+            </el-row>
+            <el-row>
+                <el-table :data="pageData.records" style="width: 100%; border:1px solid #EFEFEF;">
+                    <el-table-column prop="id" label="ID" width="150">
+                        <template #default="scope">
+                            <span>{{ scope.row.id }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="event_name" label="检查项目" width="150">
+                        <template #default="scope">
+                            <span>{{ scope.row.event_name }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="event_name" label="对象" width="250">
+                        <template #default="scope">
+                            <span v-if="scope"> {{ scope.row.event_data.name }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="操作" width="150">
+                        <template #default="scope">
+                            <el-button type="success" plain @click="handleShowReportEvent(scope.row)">查看</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </el-row>
+            <el-row>
+                <el-pagination background layout="prev, pager, next" :total="pageData.total"
+                    :page-size="pageData.page_size" @current-change="handleCurrentPageChange"
+                    :current-page.sync="pageData.page">
+                </el-pagination>
+            </el-row>
+        </el-col>
+        <el-col :span="4">
+            <el-row v-if="graphData.status == GraphStatus.GRAPH_STATUS_CHECKING">
+                正在检查中
+                <el-progress :percentage="progressValue" style="width:250px;"></el-progress>
+            </el-row>
+
+            <el-menu default-active="1" mode="vertical" @select="handleMenuSelect">
+                <el-menu-item index="1" v-if="graphData.status == GraphStatus.GRAPH_STATUS_NORMAL">
+                    <span slot="title">
+                        开始检查
+                    </span>
+                </el-menu-item>
+                <el-menu-item index="1" v-if="graphData.status == GraphStatus.GRAPH_STATUS_CHECKED">
+                    <span slot="title">
+                        重新开始检查
+                    </span>
+                </el-menu-item>
+            </el-menu>
+        </el-col>
+    </el-row>
+
+
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch, onUnmounted } from 'vue'
+import type { TabsInstance } from 'element-plus'
+import TableRelation from './TableRelation.vue'
+import TableIndividualNode from './TableIndividualNode.vue'
+import TableContradictions from './TableContradictions.vue'
+import { ElMessageBox, ElNotification } from 'element-plus'
+import type { GraphData } from '@/api/AgentApi'
+import {
+    createGraphCheckReport, getGraph, GraphStatus,
+    getGraphCheckProgress, getGraphCheckProgressValue, getGraphCheckProgressData,
+    getGraphCheckReport, getGraphCheckReportEvents
+} from '@/api/GraphApi'
+
+import GraphNodeDialog from '@/dialogs/GraphNodeDialog.vue'
+import { set } from 'date-fns'
+
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0 }
+
+})
+watch(() => props.graphId, () => {
+    loadGraph()
+})
+interface ProgressData {
+    nodes_count: number;
+    edges_count: number;
+    processed_nodes_count: number;
+    processed_edges_count: number;
+}
+interface PageData {
+    page: number;
+    pages: number;
+    page_size: number;
+    total: number;
+    records: GraphData[];
+    event_names: string[];
+}
+interface ReportEventData {
+    event_code: string
+    event_data: any
+}
+interface EventSearchData {
+    event_name: string
+}
+const searchForm = ref<EventSearchData>({ event_name: "" })
+const tabPosition = ref<TabsInstance['tabPosition']>('left')
+const graphData = ref<GraphData>({} as GraphData)
+const reportData = ref<any>({})
+const progressValue = ref(0)
+const progressData = ref<ProgressData>({} as ProgressData)
+const graphNodeDialog = ref<InstanceType<typeof GraphNodeDialog>>()
+const graphNodeDialogTitle = ref("")
+const pageData = ref<PageData>({ page: 1, pages: 1, page_size: 10, total: 0, records: [], event_names: [] })
+var timer: number = 0
+function handleMenuSelect(key: string, keyPath: string) {
+
+    ElMessageBox.confirm(
+        `创建图谱的检查报告需要2-30分钟,具体取决于节点和关系的数量,是否开始?`,
+        '提示',
+    ).then(() => {
+
+        createGraphCheckReport({ graph_id: props.graphId }).then((res) => {
+            ElNotification({
+                title: '提示',
+                message: '检查报告已经开始,请稍后查看',
+                type: 'success',
+            })
+        })
+        graphData.value.status = GraphStatus.GRAPH_STATUS_CHECKING
+        if (timer == 0) {
+            clearInterval(timer)
+            timer = setInterval(loadCheckingProgress, 3000)
+
+        }
+    })
+}
+function handleShowReportEvent(data: any) {
+    console.log(data)
+    var report_content: ReportEventData = JSON.parse(data.event_content)
+    if (report_content.event_code == "isolated_node") {
+        if (graphNodeDialog.value) {
+            graphNodeDialogTitle.value = "孤立节点"
+            graphNodeDialog.value.showDialog(true, report_content.event_data.id)
+        } else {
+            ElNotification({
+                title: '提示',
+                message: '孤立节点',
+                type: 'success',
+            })
+        }
+    }
+    if (report_content.event_code == "multiple_node_types") {
+        ElMessageBox.confirm(
+            `节点 ${report_content.event_data.name} 有多个类型: ${report_content.event_data.types}`,
+        )
+    }
+}
+
+function handleSearch() {
+    pageData.value.page = 1
+    pageData.value.pages = 0
+    pageData.value.total = 0
+    pageData.value.records = []
+    loadGraphCheckReportEvents()
+}
+function handleSearchReset() {
+    pageData.value.page = 1
+    pageData.value.pages = 0
+    pageData.value.total = 0
+    pageData.value.records = []
+    searchForm.value = { event_name: "" }
+    loadGraphCheckReportEvents()
+}
+const handleRefreshTable = () => {
+    handleCurrentPageChange(pageData.value.page)
+}
+const loadCheckingProgress = () => {
+    console.log("loadCheckingProgress")
+    getGraphCheckProgress(props.graphId).then((res) => {
+        reportData.value = res.records[0]
+        if (reportData.value == undefined) {
+            return
+        }
+        if (!reportData.value.report_content) {
+            return
+        }
+        var jsonData = JSON.parse(reportData.value.report_content)
+        if (jsonData) {
+            progressValue.value = getGraphCheckProgressValue(jsonData)
+            progressData.value = getGraphCheckProgressData(jsonData) as ProgressData
+            console.log(progressValue.value)
+            if (progressValue.value == 100) {
+                clearInterval(timer)
+                timer = 0
+                graphData.value.status = GraphStatus.GRAPH_STATUS_CHECKED
+                ElNotification({
+                    title: '提示',
+                    message: '检查报告已经完成',
+                    type: 'success',
+                })
+            }
+        }
+    })
+}
+function handleCurrentPageChange(page: number) {
+    pageData.value.page = page
+    loadGraphCheckReportEvents()
+}
+const loadGraphCheckReportEvents = () => {
+    getGraphCheckReportEvents(props.graphId, pageData.value.page, pageData.value.page_size, searchForm.value.event_name).then((res) => {
+        if (res.records) {
+            pageData.value.records = res.records
+            pageData.value.pages = res.meta.pages
+            pageData.value.total = res.meta.total
+            pageData.value.event_names = res.meta.event_names
+            pageData.value.records.forEach((item: any) => {
+                var report_content: ReportEventData = JSON.parse(item.event_content)
+                item.event_data = report_content.event_data
+            })
+        }
+    })
+}
+const loadGraph = () => {
+    if (props.graphId == 0) {
+        return
+    }
+    getGraph(props.graphId).then((res) => {
+        graphData.value = res.records[0]
+        if (graphData.value.status == GraphStatus.GRAPH_STATUS_NEED_CHECK || graphData.value.status == GraphStatus.GRAPH_STATUS_CHECKING) {
+            timer = setInterval(loadCheckingProgress, 3000)
+        }
+        if (graphData.value.status == GraphStatus.GRAPH_STATUS_CHECKED) {
+            loadGraphCheckReportEvents()
+        }
+    })
+}
+onMounted(() => {
+    loadGraph()
+})
+
+onUnmounted(() => {
+    if (timer) {
+        clearInterval(timer)
+    }
+})
+</script>

+ 119 - 0
src/components/PanelGraphNodesEdgesMgr.vue

@@ -0,0 +1,119 @@
+<template>
+<GraphNodeDialog ref="graphNodeDialog" :title="graphNodeDialogTitle" :graph-id="graphId" ></GraphNodeDialog>
+
+<div style="display: flex; flex-direction: row;">
+                    <el-select v-model="formData.searchType" style="width: 150px">
+                        <el-option label="节点" value="node"></el-option>
+                        <el-option label="关系" value="edge"></el-option>
+                    </el-select>    
+                    <el-input style="width: 240px" v-model="formData.searchText" placeholder="请输入你要搜索的节点" />
+                    <el-button type="primary" @click="handleSearch()">节点搜索</el-button>
+</div>
+                
+                <el-table v-if="formData.searchType=='node'" :data="graphNodes"  height="650" style="width: 100%"  @current-change="handleCurrentChange">
+                <el-table-column prop="id" label="ID" width="180"></el-table-column>
+                <el-table-column prop="name" label="名称"></el-table-column>
+                <el-table-column prop="category" label="类型"></el-table-column>
+
+                </el-table>
+                
+                <el-table v-if="formData.searchType=='edge'" :data="graphEdges"  height="650" style="width: 100%" >
+                <el-table-column prop="id" label="ID" width="180"></el-table-column>
+                <el-table-column prop="name" label="名称"></el-table-column>
+                <el-table-column prop="category" label="类型"></el-table-column>
+
+                </el-table>
+</template>
+<script setup lang="ts">
+
+import { ref, computed, onMounted } from 'vue'
+import  GraphNodeDialog  from '@/dialogs/GraphNodeDialog.vue'
+import { ElNotification } from 'element-plus'
+import { searchNodes, searchEdges } from '@/api/GraphApi'
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0}
+  
+})
+const graphNodeDialog=ref()
+const graphNodeDialogTitle = ref("节点详情")
+const formData = ref({
+    searchType: 'node',
+    searchText: '%',
+})
+const graphNodes = ref([])
+const graphEdges = ref([])
+
+const handleSearch = () => {
+    console.log("handleSearch", formData.value.searchType, formData.value.searchText)
+    if (formData.value.searchType == 'node') {        
+        loadGraphNodes(formData.value.searchText) 
+    } else {
+ 
+        loadGraphEdges(formData.value.searchText) 
+    }
+}
+
+const loadGraphEdges = (searchKey:string) => {
+    console.log("loadGraphEdges", searchKey, props.graphId) 
+    if (props.graphId == 0 || props.graphId == undefined) {
+        ElNotification({
+            title: 'Error',
+            message: 'Graph ID is required',
+            type: 'error', 
+        })
+    }
+    if (searchKey == '') {
+        ElNotification({
+            title: 'Error',
+            message: 'Search key is required',
+            type: 'error',
+        }) 
+        return
+    }
+    
+    searchEdges(searchKey, props.graphId).then((res:any) => {
+        graphEdges.value = res.records
+    })
+}
+const loadGraphNodes = (searchKey:string) => {
+    console.log("loadGraphNodes", searchKey, props.graphId)
+    if (props.graphId == 0 || props.graphId == undefined) {
+        ElNotification({
+            title: 'Error',
+            message: 'Graph ID is required',
+            type: 'error', 
+        })
+    }
+    if (searchKey == '') {
+        ElNotification({
+            title: 'Error',
+            message: 'Search key is required',
+            type: 'error',
+        }) 
+        return
+    }
+    
+    searchNodes(searchKey, props.graphId).then((res:any) => {
+        graphNodes.value = res.records
+    })
+}
+const handleCurrentChange = (val:any) => {
+    if (val == null) return
+    console.log("handleCurrentChange", val)
+    graphNodeDialog.value.showDialog(true, val.id)
+}
+onMounted(() => {
+    console.log("onMounted", props.graphId)
+    setTimeout(() => {
+            
+        if (props.graphId == 0 || props.graphId == undefined) {
+            return
+        }
+        if (formData.value.searchType == 'node') {
+            loadGraphNodes(formData.value.searchText) 
+        } else {
+            loadGraphEdges(formData.value.searchText) 
+        }
+    }, 1000)
+})
+</script>

+ 80 - 0
src/components/TableContradictions.vue

@@ -0,0 +1,80 @@
+<template>
+   
+    <el-row>
+        <el-table :data="nodesData" style="height: 500px;" v-loading="dataLoading">
+            <el-table-column prop="src_name" label="节点1" width="150">
+                <template #default="scope">
+                    {{scope.row.src_name}}
+                </template>
+            </el-table-column>
+            <el-table-column prop="edges" label="关系" width="250">
+                <template  #default="scope" >
+                    <div v-for="edge in scope.row.edges" :key="edge.id" style="display: flex; flex-direction: row;">   
+                          <div v-if="edge.src_id == scope.row.src_id">
+                                    ---
+                            </div>
+                                <div v-else>
+                                    &lt;--
+                                </div>
+                                <div>{{edge.name}}&nbsp;      <a>删除</a></div>
+                                <div v-if="edge.src_id == scope.row.src_id">                                    
+                                    --&gt;
+                                </div>
+                                <div v-else>
+                                    --
+                                </div>
+                    </div>
+                </template>
+
+            </el-table-column>
+            <el-table-column prop="dest_name" label="节点2"  width="150">
+                <template #default="scope">
+                    {{scope.row.dest_name}}
+                </template>
+            </el-table-column>
+
+        </el-table> 
+    </el-row>
+    <el-row>
+    <el-button type="primary" >删除所有孤立节点</el-button>
+    </el-row>
+
+</template>
+
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch } from 'vue'
+import type { TabsInstance } from 'element-plus'
+import TableRelation from './TableRelation.vue'
+
+import type { ContradictionData } from '@/types/dataset'
+const nodesData = ref<ContradictionData[]>([])
+const dataLoading = ref(false)
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0}
+  
+  
+})
+
+watch(props, (newVal, oldVal) => {
+    console.log('props changed:', newVal, oldVal)
+    if (newVal.graphId > 0) {
+        nodesData.value = []; // 清空数据
+        loadData(); // 重新加载数据
+    }
+})
+const loadData = () =>{
+    // if (props.graphId <= 0) return;
+    // dataLoading.value = true;
+    // getContradictions(props.graphId).then((res:any) => {
+    //     res.records.forEach((item:any) => {
+    //         nodesData.value.push({...item}) 
+    //     })
+    //     dataLoading.value = false;
+    // })
+}
+onMounted(() => {
+    console.log('graphId:',props.graphId)
+    //loadData()
+})
+</script>

+ 55 - 0
src/components/TableIndividualNode.vue

@@ -0,0 +1,55 @@
+<template>
+    <el-row>以下是图谱中孤立的节点:</el-row>
+    <el-row>
+        <el-table :data="nodesData" style="width: 500px;height: 500px;" v-loading="dataLoading">
+            <el-table-column prop="name" label="名称"></el-table-column>
+            <el-table-column prop="category" label="类型"></el-table-column>
+            <el-table-column prop="id">
+                <template #default="scope">
+                    <a>删除</a>
+                    </template>
+            </el-table-column>
+        </el-table> 
+    </el-row>
+    <el-row>
+    <el-button type="primary" >删除所有孤立节点</el-button>
+    </el-row>
+
+</template>
+
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch } from 'vue'
+import type { TabsInstance } from 'element-plus'
+import TableRelation from './TableRelation.vue'
+import type { NodesData } from '@/types/dataset'
+const nodesData = ref<NodesData[]>([])
+const dataLoading = ref(false)
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0}
+  
+  
+})
+
+watch(props, (newVal, oldVal) => {
+    console.log('props changed:', newVal, oldVal)
+    if (newVal.graphId > 0) {
+        nodesData.value = []; // 清空数据
+        loadData(); // 重新加载数据
+    }
+})
+const loadData = () =>{
+    // if (props.graphId <= 0) return;
+    // dataLoading.value = true;
+    // getIndividualNodes(props.graphId).then((res:any) => {
+    //     res.records.forEach((item:any) => {
+    //         nodesData.value.push({...item}) 
+    //     })
+    //     dataLoading.value = false;
+    // })
+}
+onMounted(() => {
+    console.log('graphId:',props.graphId)
+    //loadData()
+})
+</script>

+ 15 - 0
src/components/TableRelation.vue

@@ -0,0 +1,15 @@
+<template>
+    
+</template>
+
+
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue'
+import type { TabsInstance } from 'element-plus'
+import TableRelation from './TableRelation.vue'
+const props = defineProps({
+    graphId: { type: Number, required: true, default: 0}
+  
+})
+
+</script>

+ 1 - 1
src/dialogs/OCRDialog.vue

@@ -506,8 +506,8 @@ async function calculateFileHash(file: File) {
 async function handleImportFiles(filesList: any[]) {
   loading.value = true
   await Promise.allSettled(filesList.map(async (fileInfo) => {
+    const { file_name, minio_url, file_url } = fileInfo;
     try {
-      const { file_name, minio_url, file_url } = fileInfo;
       const response = await axios.get(file_url, { responseType: 'blob' });
       const rawFile = new File([response.data], file_name, { type: response.data.type });
       const fileHash = await calculateFileHash(rawFile);

+ 56 - 77
src/views/GraphManagement.vue

@@ -1,52 +1,54 @@
 <template>
-    <GraphNodeDialog ref="graphNodeDialog" :title="'节点详情'" :graph-id="graphId"></GraphNodeDialog>
+
     <div>
         <el-row>
-            <span class="view-title">图谱数据管理</span>
+            <el-page-header @back="handleGoBack" :content="graphSummary.graph_data.name" title="返回">
+
+            </el-page-header>
+            <div style="position:absolute;right:0 ;"><b>图谱数据管理</b></div>
+            <el-divider></el-divider>
         </el-row>
         <el-row>
-            <el-tabs v-model="activeName" @tab-click="handleClick" style="width: 100%;">
+        </el-row>
+        <el-row>
+            <el-tabs v-model="tabActivated" style="width: 100%;">
                 <el-tab-pane label="基本信息" name="tab1">
-                    <el-collapse accordion>
-                        <el-collapse-item name="1">
-                            <template #title><span style="color:blue;">图谱数据概览</span></template>
-                            当前图谱共有 {{ graphSummary.nodes_count }} 个节点,{{ graphSummary.edges_count }} 条边<br>
-
-                        </el-collapse-item>
-
-                        <el-collapse-item name="2">
-                            <template #title><span style="color:blue;">节点类型</span></template>
-                            节点类型包括:{{ graphSummary.nodes_categories.join(', ') }}<br></el-collapse-item>
-
-                        <el-collapse-item name="3">
-                            <template #title><span style="color:blue;">边类型</span></template>
-                            边类型包括:{{ graphSummary.edges_categories.join(', ') }}<br></el-collapse-item>
-                    </el-collapse>
+                    <span style="color:blue;">图谱数据概览</span>
+                    当前图谱共有 {{ graphSummary.nodes_count }} 个节点,{{ graphSummary.edges_count }} 条边<br>
                 </el-tab-pane>
-                <el-tab-pane label="节点搜索" name="tab2">
-                    <div style="display: flex; flex-direction: row;">
-                        <el-input style="width: 240px" v-model="formData.searchText" placeholder="请输入你要搜索的节点" />
-                        <el-button type="primary" @click="loadGraphNodes(formData.searchText)">节点搜索</el-button>
-                    </div>
 
-                    <el-table :data="graphNodes" height="650" style="width: 100%" @current-change="handleCurrentChange">
-                        <el-table-column prop="id" label="ID" width="180"></el-table-column>
-                        <el-table-column prop="name" label="名称"></el-table-column>
-                        <el-table-column prop="category" label="类型"></el-table-column>
-
-                    </el-table>
+                <el-tab-pane label="类型标准化" name="tab3">
+                    <PanelGraphCategoryMgr :graph-id="graphId"></PanelGraphCategoryMgr>
                 </el-tab-pane>
-                <el-tab-pane label="类型整合" name="tab3">
-                    <GraphCategoryMgr :graph-id="graphId"></GraphCategoryMgr>
+                <el-tab-pane label="节点搜索" name="tab2">
+                    <PanelGraphNodesEdgesMgr :graph-id="graphId"></PanelGraphNodesEdgesMgr>
+                    <!-- <div style="display: flex; flex-direction: row;">
+                      <el-select v-model="formData.searchType" style="width: 150px">
+                          <el-option label="节点" value="node"></el-option>
+                          <el-option label="关系" value="edge"></el-option>
+                      </el-select>    
+                      <el-input style="width: 240px" v-model="formData.searchText" placeholder="请输入你要搜索的节点" />
+                      <el-button type="primary" @click="handleSearch()">节点搜索</el-button>
+                  </div>
+                  
+                  <el-table v-if="formData.searchType=='node'" :data="graphNodes"  height="650" style="width: 100%"  @current-change="handleCurrentChange">
+                  <el-table-column prop="id" label="ID" width="180"></el-table-column>
+                  <el-table-column prop="name" label="名称"></el-table-column>
+                  <el-table-column prop="category" label="类型"></el-table-column>
+  
+                  </el-table>
+                  
+                  <el-table v-if="formData.searchType=='edge'" :data="graphEdges"  height="650" style="width: 100%" >
+                  <el-table-column prop="id" label="ID" width="180"></el-table-column>
+                  <el-table-column prop="name" label="名称"></el-table-column>
+                  <el-table-column prop="category" label="类型"></el-table-column>
+  
+                  </el-table> -->
                 </el-tab-pane>
                 <el-tab-pane label="术语标注" name="tab4">术语标注</el-tab-pane>
-                <el-tab-pane label="语义检查" name="tab5"><el-menu>
-                        <el-menu-item index="4-1">孤立节点</el-menu-item>
-                        <el-menu-item index="4-2">矛盾关系</el-menu-item>
-                        <el-menu-item index="4-2">循环关系</el-menu-item>
-                        <el-menu-item index="4-2">冗余关系</el-menu-item>
-                        <el-menu-item index="4-2">自动检查</el-menu-item>
-                    </el-menu></el-tab-pane>
+                <el-tab-pane label="语义检查" name="tab5">
+                    <PanelGraphCheck :graph-id="graphId"></PanelGraphCheck>
+                </el-tab-pane>
             </el-tabs>
 
             <el-col :span="18">
@@ -66,61 +68,38 @@
 import { ref, computed, onMounted } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { getSessionVar } from '@/utils/session'
-import { searchNodes, getGraphSummary } from '@/api/GraphApi'
+import { searchNodes, searchEdges, getGraphSummary } from '@/api/GraphApi'
 import { ElNotification } from 'element-plus'
-import GraphNodeDialog from '@/dialogs/GraphNodeDialog.vue'
-import GraphCategoryMgr from '@/components/GraphCategoryMgr.vue'
-let activeName = ref("")
-const graphNodeDialog = ref()
+
+import PanelGraphCategoryMgr from '@/components/PanelGraphCategoryMgr.vue'
+import PanelGraphNodesEdgesMgr from '@/components/PanelGraphNodesEdgesMgr.vue'
+import PanelGraphCheck from '@/components/PanelGraphCheck.vue'
+
 const formData = ref({
+    searchType: 'node',
     searchText: '%',
 })
+const tabActivated = ref('tab1')
 const graphId = ref(0)
-const graphSummary = ref({ nodes_count: 0, edges_count: 0, nodes_categories: [], edges_categories: [] })
-const currentNode = ref({ id: 0 })
-const graphNodes = ref([])
+const router = useRouter()
+const graphSummary = ref({ graph_data: { name: "" }, nodes_count: 0, edges_count: 0, nodes_categories: [], edges_categories: [] })
+
 onMounted(() => {
     var route = useRoute()
     graphId.value = Number(route.params.id as string | "0")
-    // console.log("graphId", graphId.value)
+    console.log("graphId", graphId.value)
     loadGraphSummary()
-    loadGraphNodes(formData.value.searchText)
+
 })
-const handleCurrentChange = (val: any) => {
-    if (val == null) return
-    // console.log("handleCurrentChange", val)
-    graphNodeDialog.value.showDialog(true, val.id)
+function handleGoBack() {
+    router.push({ name: "graph" })
 }
 const loadGraphSummary = () => {
-    // console.log("loadGraphSummary", graphId.value) 
+    console.log("loadGraphSummary", graphId.value)
     getGraphSummary({ graph_id: graphId.value }).then((res: any) => {
         graphSummary.value = res.records[0]
     })
 }
-function handleClick() {
-
-}
-const loadGraphNodes = (searchKey: string) => {
-    // console.log("loadGraphNodes", searchKey, graphId.value)
-    if (graphId.value == 0 || graphId.value == undefined) {
-        ElNotification({
-            title: 'Error',
-            message: 'Graph ID is required',
-            type: 'error',
-        })
-    }
-    if (searchKey == '') {
-        ElNotification({
-            title: 'Error',
-            message: 'Search key is required',
-            type: 'error',
-        })
-        return
-    }
-    searchNodes(searchKey, graphId.value).then((res: any) => {
-        graphNodes.value = res.records
-    })
-}
 
 </script>
 <style scoped>