Browse Source

关系列表初步

fuyou 7 months ago
parent
commit
d1d9c0515d
2 changed files with 231 additions and 267 deletions
  1. 2 1
      autoremark-ui/src/api/business/relation.js
  2. 229 266
      autoremark-ui/src/views/index.vue

+ 2 - 1
autoremark-ui/src/api/business/relation.js

@@ -50,4 +50,5 @@ export function exportRelation(query) {
     method: 'get',
     params: query
   })
-}
+}
+

+ 229 - 266
autoremark-ui/src/views/index.vue

@@ -1,149 +1,135 @@
 <template>
   <div id="app">
     <el-container style="padding: 20px;">
-      <el-header>
-        <h1 style="text-align: center;">文本标注工具</h1>
-      </el-header>
-
       <el-main>
-
-        <el-row>
-
-<!--          <div>-->
-<!--            <p>Colors: {{ colors }}</p>-->
-<!--          </div>-->
-
-<!--          <div>-->
-<!--            <p>EntityTypeName: {{ entityTypeName }}</p>-->
-<!--          </div>-->
-
-          <!-- 动态生成颜色按钮 -->
-          <el-col v-for="(pair, index) in ColorEntityPair" :key="index" :span="4">
-            <el-button
-              type="primary"
-              :style="{ backgroundColor: pair.color, borderColor: pair.color }"
-              @click="setHighlightColor(pair.color)"
-            >
-              {{ pair.name }}
-            </el-button>
-          </el-col>
-
-          <!-- 如果 colors 没有加载,显示加载提示 -->
-          <el-col v-if="!isColorsLoaded" :span="24">
-            <p>加载颜色数据中...</p>
-          </el-col>
-
-          <el-col :span="4">
-            <el-button
-              type="primary"
-              style="background-color: #C0C0C0; border-color: #FFFFFF;"
-              @click="submit"
-            >
-              确认标记
-            </el-button>
+        <el-row :gutter="20">
+          <!-- 左侧内容 -->
+          <el-col :span="18">
+            <!-- 动态生成颜色按钮 -->
+            <el-row>
+              <el-col v-for="(pair, index) in ColorEntityPair" :key="index" :span="4">
+                <el-button
+                  type="primary"
+                  :style="{ backgroundColor: pair.color, borderColor: pair.color }"
+                  @click="setHighlightColor(pair.color)"
+                >
+                  {{ pair.name }}
+                </el-button>
+              </el-col>
+
+              <!-- 如果 colors 没有加载,显示加载提示 -->
+              <el-col v-if="!isColorsLoaded" :span="24">
+                <p>加载颜色数据中...</p>
+              </el-col>
+
+              <el-col :span="4">
+                <el-button
+                  type="primary"
+                  style="background-color: #C0C0C0; border-color: #FFFFFF;"
+                  @click="submit"
+                >
+                  确认标记
+                </el-button>
+              </el-col>
+
+              <el-col :span="4">
+                <input type="file" @change="handleFileUpload" accept=".txt" style="display: none;" ref="fileInput">
+                <el-button type="primary" @click="triggerFileUpload" style="margin-left: 10px;">上传文件</el-button>
+              </el-col>
+            </el-row>
+
+            <!-- 标注文本区域 -->
+            <el-row style="margin-top: 20px;">
+              <el-col :span="24">
+                <div
+                  id="textInput"
+                  contenteditable="true"
+                  @mouseup="handleText"
+                  v-html="highlightedText"
+                  style="border: 1px solid #ddd; padding: 10px; min-height: 100px;"
+                ></div>
+              </el-col>
+            </el-row>
+
+            <!-- 选定内容、开始位置、结束位置 -->
+            <el-row style="margin-top: 20px;">
+              <el-col :span="8">
+                <h3>选定内容</h3>
+                <input type="text" v-model="selectedText" readonly>
+              </el-col>
+              <el-col :span="8">
+                <h3>开始位置</h3>
+                <input type="text" v-model="startOffset" readonly>
+              </el-col>
+              <el-col :span="8">
+                <h3>结束位置</h3>
+                <input type="text" v-model="endOffset" readonly>
+              </el-col>
+            </el-row>
           </el-col>
 
-          <el-col :span="4">
-            <!--            <h3 style="display: inline-block; margin-right: 20px;">标注文本</h3>-->
-            <!--            <el-button type="primary" @click="highlightText">高亮标注</el-button>-->
-            <!--            <el-color-picker v-model="selectedColor" style="margin-left: 10px;"></el-color-picker>-->
-            <input type="file" @change="handleFileUpload" accept=".txt" style="display: none;" ref="fileInput">
-            <el-button type="primary" @click="triggerFileUpload" style="margin-left: 10px;">上传文件</el-button>
+          <!-- 右侧窗口 -->
+          <el-col :span="6">
+            <!-- 信息与历史记录窗口 -->
+            <el-card style="height: 50vh; margin-bottom: 10px;">
+              <div slot="header" class="clearfix">
+                <span>信息与历史记录</span>
+              </div>
+              <el-tabs type="border-card" style="height: calc(100% - 60px);">
+                <el-tab-pane label="信息">
+                  <p>当前标注信息:</p>
+                  <ul>
+                    <li v-for="entity in entities" :key="entity.id">
+                      {{ entity.text }}({{ entity.label }},位置:{{ entity.startOffset }}-{{ entity.endOffset }})
+                    </li>
+                  </ul>
+                </el-tab-pane>
+                <el-tab-pane label="历史记录">
+                  <p>历史记录:</p>
+                  <ul>
+                    <li v-for="(log, index) in historyLogs" :key="index">
+                      {{ log }}
+                    </li>
+                  </ul>
+                </el-tab-pane>
+              </el-tabs>
+            </el-card>
+
+            <!-- 关系窗口 -->
+            <el-card style="height: 50vh;">
+              <div slot="header" class="clearfix">
+                <span>关系</span>
+              </div>
+              <el-tabs type="border-card" style="height: calc(100% - 60px);">
+                <el-tab-pane label="关系列表">
+                  <el-row>
+                    <el-col :span="24">
+                      <el-select v-model="headEntityId" placeholder="选择头实体" style="width: 100%; margin-bottom: 10px;">
+                        <el-option v-for="entity in entities" :key="entity.id" :label="entity.text" :value="entity.id"></el-option>
+                      </el-select>
+                    </el-col>
+                    <el-col :span="24">
+                      <el-select v-model="tailEntityId" placeholder="选择尾实体" style="width: 100%; margin-bottom: 10px;">
+                        <el-option v-for="entity in entities" :key="entity.id" :label="entity.text" :value="entity.id"></el-option>
+                      </el-select>
+                    </el-col>
+                    <el-col :span="24">
+                      <el-input v-model="currentRelationType" placeholder="输入关系名称" style="width: 100%; margin-bottom: 10px;"></el-input>
+                    </el-col>
+                    <el-col :span="24">
+                      <el-button type="primary" @click="saveRelation" style="width: 100%;">保存关系</el-button>
+                    </el-col>
+                  </el-row>
+                  <ul>
+                    <li v-for="relation in relations" :key="relation.id">
+                      {{ getEntityText(relation.headEntityId) }} -> {{ getEntityText(relation.tailEntityId) }}({{ relation.type }})
+                    </li>
+                  </ul>
+                </el-tab-pane>
+              </el-tabs>
+            </el-card>
           </el-col>
         </el-row>
-
-        <!-- 标注文本和操作按钮 -->
-        <!-- 标注文本区域 -->
-        <el-row style="margin-top: 20px;">
-          <el-col :span="24">
-            <div
-              id="textInput"
-              contenteditable="true"
-              @mouseup="handleText"
-              v-html="highlightedText"
-              style="border: 1px solid #ddd; padding: 10px; min-height: 100px;"
-            ></div>
-          </el-col>
-        </el-row>
-
-        <!-- 选定内容、开始位置、结束位置 -->
-        <el-row style="margin-top: 20px;">
-          <el-col :span="8">
-            <h3>选定内容</h3>
-            <input type="text" v-model="selectedText" readonly>
-          </el-col>
-          <el-col :span="8">
-            <h3>开始位置</h3>
-            <input type="text" v-model="startOffset" readonly>
-          </el-col>
-          <el-col :span="8">
-            <h3>结束位置</h3>
-            <input type="text" v-model="endOffset" readonly>
-          </el-col>
-        </el-row>
-
-        <!-- 右侧窗口 -->
-        <el-col :span="6" style="height: 100%;">
-          <!-- 信息与历史记录窗口 -->
-          <el-card style="height: 50%; margin-bottom: 10px;">
-            <div slot="header" class="clearfix">
-              <span>信息与历史记录</span>
-            </div>
-            <el-tabs type="border-card" style="height: calc(100% - 60px);">
-              <el-tab-pane label="信息">
-                <p>当前标注信息:</p>
-                <ul>
-                  <li v-for="entity in entities" :key="entity.id">
-                    {{ entity.text }}({{ entity.label }},位置:{{ entity.startOffset }}-{{ entity.endOffset }})
-                  </li>
-                </ul>
-              </el-tab-pane>
-              <el-tab-pane label="历史记录">
-                <p>历史记录:</p>
-                <ul>
-                  <li v-for="(log, index) in historyLogs" :key="index">
-                    {{ log }}
-                  </li>
-                </ul>
-              </el-tab-pane>
-            </el-tabs>
-          </el-card>
-
-          <!-- 关系窗口 -->
-          <el-card style="height: 50%;">
-            <div slot="header" class="clearfix">
-              <span>关系</span>
-            </div>
-            <el-tabs type="border-card" style="height: calc(100% - 60px);">
-              <el-tab-pane label="关系列表">
-                <el-row>
-                  <el-col :span="24">
-                    <el-select v-model="headEntityId" placeholder="选择头实体" style="width: 100%; margin-bottom: 10px;">
-                      <el-option v-for="entity in entities" :key="entity.id" :label="entity.text" :value="entity.id"></el-option>
-                    </el-select>
-                  </el-col>
-                  <el-col :span="24">
-                    <el-select v-model="tailEntityId" placeholder="选择尾实体" style="width: 100%; margin-bottom: 10px;">
-                      <el-option v-for="entity in entities" :key="entity.id" :label="entity.text" :value="entity.id"></el-option>
-                    </el-select>
-                  </el-col>
-                  <el-col :span="24">
-                    <el-input v-model="currentRelationType" placeholder="输入关系名称" style="width: 100%; margin-bottom: 10px;"></el-input>
-                  </el-col>
-                  <el-col :span="24">
-                    <el-button type="primary" @click="saveRelation" style="width: 100%;">保存关系</el-button>
-                  </el-col>
-                </el-row>
-                <ul>
-                  <li v-for="relation in relations" :key="relation.id">
-                    {{ getEntityText(relation.headEntityId) }} -> {{ getEntityText(relation.tailEntityId) }}({{ relation.type }})
-                  </li>
-                </ul>
-              </el-tab-pane>
-            </el-tabs>
-          </el-card>
-        </el-col>
-
       </el-main>
     </el-container>
   </div>
@@ -151,7 +137,9 @@
 
 <script>
 import { listEntity_type, getEntity_type, delEntity_type, addEntity_type, updateEntity_type, findAll} from "@/api/business/entity_type";
-import { listEntity, getEntity, delEntity, addEntity, updateEntity } from "@/api/business/entity";
+import { listEntity, getEntity, delEntity, addEntity, updateEntity } from "@/api/business/entity"
+import { listRelation, getRelation, addRelation, updateRelation, delRelation, exportRelation} from "@/api/business/relation";
+import entity from "@/views/business/entity/index.vue";
 export default {
   name: 'AutoRemark',
   data() {
@@ -227,7 +215,11 @@ export default {
         }
       }
       this.currentColorId = this.colorIds[index];
-      console.log(this.currentColorId)
+      console.log(this.currentColorId);
+
+      // 添加历史记录:选择实体类型
+      const entityTypeName = this.entityTypeName[index];
+      this.historyLogs.push(`选择实体类型:${entityTypeName}`);
     },
     // 触发文件上传
     triggerFileUpload() {
@@ -255,28 +247,6 @@ export default {
         });
       }
     },
-    // highlightText() {
-    //   if (this.selectedText) {
-    //     // 创建实体对象
-    //     const entity = {
-    //       id: `entity-${this.entities.length + 1}`, // 生成唯一ID
-    //       text: this.selectedText,
-    //       startOffset: this.startOffset,
-    //       endOffset: this.endOffset,
-    //       label: this.selectedLabel,
-    //       color: this.selectedColor // 保存标注颜色
-    //     };
-    //     this.entities.push(entity); // 保存实体
-    //
-    //     // 高亮显示
-    //     this.updateHighlightedText();
-    //   } else {
-    //     this.$message({
-    //       message: '请先选择文本!',
-    //       type: 'warning'
-    //     });
-    //   }
-    // },
     highlightText() {
       if (this.currentHighlightColor === '') {
         this.$message({
@@ -289,12 +259,28 @@ export default {
       const selectedText = window.getSelection().toString(); // 获取选中的文本
 
       if (selectedText) {
+
+        // 创建实体对象
+        const entity = {
+          id: `entity-${this.entities.length + 1}`, // 生成唯一ID
+          text: selectedText,
+          startOffset: this.startOffset,
+          endOffset: this.endOffset,
+          label: this.selectedLabel,
+          color: this.currentHighlightColor // 保存标注颜色
+        };
+        this.entities.push(entity); // 保存实体
+
+        // 高亮显示
         const highlightedText = `<span style="background-color: ${this.currentHighlightColor}; color: black;">${selectedText}</span>`;
 
         // 替换原文中的选中文本
         this.highlightedText = this.highlightedText.replace(selectedText, highlightedText);
         // 选中实体完毕可以进行提交
         this.flag = 1;
+
+        // 添加历史记录:高亮文本
+        this.historyLogs.push(`高亮文本:${selectedText},开始位置:${this.startOffset},结束位置:${this.endOffset}`);
       } else {
         this.$message({
           message: '请先选择文本!',
@@ -342,17 +328,58 @@ export default {
     },
     saveRelation() {
       if (this.headEntityId && this.tailEntityId && this.currentRelationType) {
+        // 检查头实体和尾实体是否已经保存到实体表中
+        const headEntity = this.entities.find(entity => entity.id === this.headEntityId);
+        const tailEntity = this.entities.find(entity => entity.id === this.tailEntityId);
+
+        if (!headEntity || !tailEntity) {
+          this.$message({
+            message: '请先保存头实体和尾实体到实体表中!',
+            type: 'warning'
+          });
+          return;
+        }
+
         // 创建关系对象
         const relation = {
-          id: `relation-${this.relations.length + 1}`, // 生成唯一ID
           headEntityId: this.headEntityId,
           tailEntityId: this.tailEntityId,
           type: this.currentRelationType
         };
-        this.relations.push(relation); // 保存关系
-        this.$message({
-          message: '关系保存成功!',
-          type: 'success'
+
+        // 调用 addRelation 接口保存关系
+        addRelation(relation).then(response => {
+          if (response.code === 200) {
+            // 保存成功,将关系添加到前端的关系列表中
+            this.relations.push({
+              id: response.data.id,
+              ...relation
+            });
+
+            this.$message({
+              message: '关系保存成功!',
+              type: 'success'
+            });
+
+            // 添加历史记录:保存关系
+            const headEntityText = this.getEntityText(relation.headEntityId);
+            const tailEntityText = this.getEntityText(relation.tailEntityId);
+            this.historyLogs.push(`保存关系:${headEntityText} -> ${tailEntityText}(${relation.type})`);
+
+            // 获取关系的详细信息
+            this.fetchRelationEntities(response.data.id);
+          } else {
+            this.$message({
+              message: '关系保存失败!',
+              type: 'error'
+            });
+          }
+        }).catch(error => {
+          console.error('保存关系失败:', error);
+          this.$message({
+            message: '关系保存失败!',
+            type: 'error'
+          });
         });
       } else {
         this.$message({
@@ -365,20 +392,44 @@ export default {
       const entity = this.entities.find(e => e.id === entityId);
       return entity ? entity.text : '未知实体';
     },
-    submit(){
-      if(this.flag === 1)
-      {
+    submit() {
+      if (this.flag === 1) {
         this.entityForm.typeId = this.currentColorId;
         this.entityForm.startOffset = this.startOffset;
         this.entityForm.endOffset = this.endOffset;
         this.entityForm.text = this.selectedText;
-        addEntity(this.entityForm);
-        this.$message({
-          message: '成功提交',
-          type: 'success'
+
+        addEntity(this.entityForm).then(response => {
+          if (response.code === 200) {
+            // 实体保存成功后,更新 entities 数组
+            const entity = {
+              id: response.data.id, // 假设后端返回保存后的实体ID
+              text: this.selectedText,
+              startOffset: this.startOffset,
+              endOffset: this.endOffset,
+              label: this.selectedLabel,
+              color: this.currentHighlightColor
+            };
+            this.entities.push(entity);
+
+            this.$message({
+              message: '成功提交',
+              type: 'success'
+            });
+          } else {
+            this.$message({
+              message: '提交失败',
+              type: 'error'
+            });
+          }
+        }).catch(error => {
+          console.error('提交失败:', error);
+          this.$message({
+            message: '提交失败',
+            type: 'error'
+          });
         });
-      }
-      else{
+      } else {
         this.$message({
           message: '请先对文本进行标注',
           type: 'warning'
@@ -389,90 +440,6 @@ export default {
 }
 </script>
 
-<!--<style>-->
-<!--#app {-->
-<!--  font-family: Avenir, Helvetica, Arial, sans-serif;-->
-<!--  -webkit-font-smoothing: antialiased;-->
-<!--  -moz-osx-font-smoothing: grayscale;-->
-<!--  color: #2c3e50;-->
-<!--  margin-top: 20px;-->
-<!--}-->
-
-<!--#textInput {-->
-<!--  width: 100%;-->
-<!--  min-height: 100px;-->
-<!--  padding: 10px;-->
-<!--  border: 1px solid #ddd;-->
-<!--  border-radius: 4px;-->
-<!--  font-size: 14px;-->
-<!--  white-space: pre-wrap; /* 保留换行和空格 */-->
-<!--}-->
-
-<!--input[readonly] {-->
-<!--  width: 100%;-->
-<!--  padding: 5px;-->
-<!--  border: 1px solid #ddd;-->
-<!--  border-radius: 4px;-->
-<!--  background-color: #f5f5f5;-->
-<!--  font-size: 14px;-->
-<!--}-->
-
-<!--.el-card {-->
-<!--  margin-bottom: 20px;-->
-<!--}-->
-
-<!--.el-row {-->
-<!--  margin-bottom: 20px;-->
-<!--}-->
-
-<!--.el-color-picker {-->
-<!--  vertical-align: middle;-->
-<!--}-->
-
-<!--.el-button {-->
-<!--  margin-left: 10px;-->
-<!--}-->
-
-<!--.el-col-6 {-->
-<!--  height: 100%;-->
-<!--}-->
-
-<!--.el-card {-->
-<!--  height: 50%;-->
-<!--  margin-bottom: 10px; /* 保持卡片之间的间距 */-->
-<!--}-->
-
-<!--.el-tabs {-->
-<!--  height: calc(100% - 60px); /* 减去标题栏高度 */-->
-<!--}-->
-
-<!--.el-tabs {-->
-<!--  margin-top: 10px;-->
-<!--}-->
-
-<!--.clearfix:before,-->
-<!--.clearfix:after {-->
-<!--  display: table;-->
-<!--  content: "";-->
-<!--}-->
-<!--.clearfix:after {-->
-<!--  clear: both;-->
-<!--}-->
-
-<!--/* 右侧窗口高度调整 */-->
-<!--.el-col-6 {-->
-<!--  height: 100%;-->
-<!--}-->
-
-<!--.el-card {-->
-<!--  height: 50%;-->
-<!--}-->
-
-<!--.el-tabs {-->
-<!--  height: calc(100% - 60px); /* 减去标题栏高度 */-->
-<!--}-->
-<!--</style>-->
-
 <style>
 #app {
   font-family: Avenir, Helvetica, Arial, sans-serif;
@@ -506,7 +473,7 @@ input[readonly] {
 }
 
 .el-card {
-  height: 50%;
+  height: 50vh; /* 使用视口高度来确保窗口占据一半空间 */
   margin-bottom: 10px; /* 保持卡片之间的间距 */
 }
 
@@ -514,10 +481,6 @@ input[readonly] {
   height: calc(100% - 60px); /* 减去标题栏高度 */
 }
 
-.el-tabs {
-  margin-top: 10px;
-}
-
 .clearfix:before,
 .clearfix:after {
   display: table;
@@ -533,7 +496,7 @@ input[readonly] {
 }
 
 .el-card {
-  height: 50%;
+  height: 50vh; /* 使用视口高度来确保窗口占据一半空间 */
 }
 
 .el-tabs {