2 次代码提交 4c77446a94 ... a3081b1b24

作者 SHA1 备注 提交日期
  fuyou a3081b1b24 文本标注和文件上传version1 7 月之前
  fuyou a332212d5e 实现文本标注和上传基础功能version1 7 月之前
共有 1 个文件被更改,包括 263 次插入0 次删除
  1. 263 0
      autoremark-ui/src/views/index.vue

+ 263 - 0
autoremark-ui/src/views/index.vue

@@ -0,0 +1,263 @@
+<template>
+  <div id="app">
+    <el-container style="padding: 20px;">
+      <el-header>
+        <h1 style="text-align: center;">文本标注工具</h1>
+      </el-header>
+
+      <el-main>
+        <!-- 标注文本和操作按钮 -->
+        <el-row>
+          <el-col :span="18">
+            <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>
+        </el-row>
+
+        <!-- 标注文本区域 -->
+        <el-row style="margin-top: 20px;">
+          <el-col :span="24">
+            <div
+              id="textInput"
+              contenteditable="true"
+              @mouseup="handleTextSelection"
+              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>
+
+        <!-- 关系标注 -->
+        <h3>关系标注</h3>
+        <el-row>
+          <el-col :span="6">
+            <el-select v-model="headEntityId" placeholder="选择头实体" style="width: 100%;">
+              <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="6">
+            <el-select v-model="tailEntityId" placeholder="选择尾实体" style="width: 100%;">
+              <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="6">
+            <el-select v-model="currentRelationType" placeholder="选择关系类型" style="width: 100%;">
+              <el-option v-for="type in relationTypes" :key="type" :label="type" :value="type"></el-option>
+            </el-select>
+          </el-col>
+          <el-col :span="6">
+            <el-button type="primary" @click="saveRelation">保存关系</el-button>
+          </el-col>
+        </el-row>
+
+        <!-- 关系列表 -->
+        <h3>关系列表</h3>
+        <ul>
+          <li v-for="relation in relations" :key="relation.id">
+            {{ getEntityText(relation.headEntityId) }} -> {{ getEntityText(relation.tailEntityId) }}({{ relation.type }})
+          </li>
+        </ul>
+      </el-main>
+    </el-container>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AutoRemark',
+  data() {
+    return {
+      text: "这是一些需要标注的文本。用户可以选择文本并标记。",
+      highlightedText: "这是一些需要标注的文本。用户可以选择文本并标记。",
+      selectedLabel: "重要",
+      selectedText: '', // 选定的内容
+      startOffset: -1, // 开始位置
+      endOffset: -1, // 结束位置
+      entities: [], // 存储所有标注的实体
+      relations: [], // 存储所有标注的关系
+      relationTypes: ['位于', '属于', '创建'], // 关系类型列表
+      headEntityId: '', // 关系中的头实体ID
+      tailEntityId: '', // 关系中的尾实体ID
+      currentRelationType: '', // 当前选择的关系类型
+      selectedColor: '#ffeb3b' // 默认高亮颜色
+    };
+  },
+  methods: {
+    // 触发文件上传
+    triggerFileUpload() {
+      this.$refs.fileInput.click();
+    },
+    // 处理文件上传
+    handleFileUpload(event) {
+      const file = event.target.files[0];
+      if (file && file.type === 'text/plain') {
+        const reader = new FileReader();
+        reader.onload = (e) => {
+          const content = e.target.result;
+          this.text = content;
+          this.highlightedText = content;
+          this.$message({
+            message: '文件上传成功!',
+            type: 'success'
+          });
+        };
+        reader.readAsText(file);
+      } else {
+        this.$message({
+          message: '请上传有效的文本文件(.txt)!',
+          type: 'warning'
+        });
+      }
+    },
+    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'
+        });
+      }
+    },
+    handleTextSelection() {
+      const textInput = document.getElementById('textInput');
+      const selection = window.getSelection();
+      if (selection.rangeCount > 0) {
+        const range = selection.getRangeAt(0);
+        this.selectedText = range.toString();
+        this.startOffset = this.getOffset(range.startContainer, range.startOffset);
+        this.endOffset = this.getOffset(range.endContainer, range.endOffset);
+      }
+    },
+    getOffset(node, offset) {
+      let totalOffset = 0;
+      const textInput = document.getElementById('textInput');
+      const walker = document.createTreeWalker(textInput, NodeFilter.SHOW_TEXT, null, false);
+      let currentNode;
+
+      while ((currentNode = walker.nextNode())) {
+        if (currentNode === node) {
+          totalOffset += offset;
+          break;
+        }
+        totalOffset += currentNode.textContent.length;
+      }
+      return totalOffset;
+    },
+    updateHighlightedText() {
+      // 初始化高亮文本
+      let highlightedText = this.text;
+
+      // 遍历所有实体,添加高亮效果
+      this.entities.forEach(entity => {
+        const highlightedEntity = `<span style="background-color: ${entity.color}; color: black;" title="${entity.label}">${entity.text}</span>`;
+        highlightedText = highlightedText.replace(entity.text, highlightedEntity);
+      });
+
+      // 更新高亮文本
+      this.highlightedText = highlightedText;
+    },
+    saveRelation() {
+      if (this.headEntityId && this.tailEntityId && this.currentRelationType) {
+        // 创建关系对象
+        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'
+        });
+      } else {
+        this.$message({
+          message: '请选择头实体、尾实体和关系类型!',
+          type: 'warning'
+        });
+      }
+    },
+    getEntityText(entityId) {
+      const entity = this.entities.find(e => e.id === entityId);
+      return entity ? entity.text : '未知实体';
+    }
+  }
+}
+</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;
+}
+</style>