Browse Source

feat(editor): 按V2设计更新右键菜单

右键菜单结构:
- 基础操作:复制、剪切、粘贴(带快捷键提示)
- AI功能:AI润色、检查拼写
- 标记引用:标记为要素(子菜单选择类型)、引用到AI助手

新增功能:
- execContextAction 函数处理各种操作
- "标记为要素"改为悬停展开子菜单形式
- 添加分割线区分不同功能组
- 子菜单样式支持
何文松 3 tuần trước cách đây
mục cha
commit
22eb68dee7
1 tập tin đã thay đổi với 144 bổ sung27 xóa
  1. 144 27
      frontend/vue-demo/src/views/Editor.vue

+ 144 - 27
frontend/vue-demo/src/views/Editor.vue

@@ -377,47 +377,90 @@
       </div>
     </div>
 
-    <!-- 右键菜单 -->
+    <!-- 右键菜单 - V2 风格 -->
     <div
       v-show="contextMenuVisible"
       class="context-menu"
       :style="{ left: contextMenuPos.x + 'px', top: contextMenuPos.y + 'px' }"
     >
-      <div class="context-menu-header" v-if="selectedText">
-        <span class="selected-preview">"{{ selectedText.slice(0, 20) }}{{ selectedText.length > 20 ? '...' : '' }}"</span>
+      <!-- 基础操作 -->
+      <div class="context-menu-item" @click="execContextAction('copy')">
+        <span class="icon">📋</span>
+        <span>复制</span>
+        <span class="shortcut">Ctrl+C</span>
       </div>
-      <div class="context-menu-section">标记为实体</div>
-      <div class="context-menu-item" @click="markAsVariable('person')" :disabled="markingEntity">
-        <span class="icon">👤</span>
-        <span>人物</span>
+      <div class="context-menu-item" @click="execContextAction('cut')">
+        <span class="icon">✂️</span>
+        <span>剪切</span>
+        <span class="shortcut">Ctrl+X</span>
       </div>
-      <div class="context-menu-item" @click="markAsVariable('org')" :disabled="markingEntity">
-        <span class="icon">🏢</span>
-        <span>组织机构</span>
+      <div class="context-menu-item" @click="execContextAction('paste')">
+        <span class="icon">📄</span>
+        <span>粘贴</span>
+        <span class="shortcut">Ctrl+V</span>
       </div>
-      <div class="context-menu-item" @click="markAsVariable('location')" :disabled="markingEntity">
-        <span class="icon">📍</span>
-        <span>地点</span>
-      </div>
-      <div class="context-menu-item" @click="markAsVariable('date')" :disabled="markingEntity">
-        <span class="icon">📅</span>
-        <span>日期/时间</span>
-      </div>
-      <div class="context-menu-item" @click="markAsVariable('data')" :disabled="markingEntity">
-        <span class="icon">📊</span>
-        <span>数据/指标</span>
+      
+      <div class="context-menu-divider"></div>
+      
+      <!-- AI 功能 -->
+      <div class="context-menu-item" @click="execContextAction('polish')">
+        <span class="icon">✨</span>
+        <span>AI 润色</span>
       </div>
-      <div class="context-menu-item" @click="markAsVariable('concept')" :disabled="markingEntity">
-        <span class="icon">💡</span>
-        <span>概念/技术</span>
+      <div class="context-menu-item" @click="execContextAction('spell')">
+        <span class="icon">📝</span>
+        <span>检查拼写</span>
       </div>
-      <div class="context-menu-item" @click="markAsVariable('entity')" :disabled="markingEntity">
+      
+      <div class="context-menu-divider"></div>
+      
+      <!-- 标记和引用 -->
+      <div class="context-menu-item has-submenu" @mouseenter="showEntitySubmenu = true" @mouseleave="showEntitySubmenu = false">
         <span class="icon">🏷️</span>
-        <span>其他实体</span>
+        <span>标记为要素</span>
+        <span class="submenu-arrow">›</span>
+        
+        <!-- 实体类型子菜单 -->
+        <div class="context-submenu" v-show="showEntitySubmenu">
+          <div class="context-menu-item" @click.stop="markAsVariable('person')" :disabled="markingEntity">
+            <span class="icon">👤</span>
+            <span>人物</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('org')" :disabled="markingEntity">
+            <span class="icon">🏢</span>
+            <span>组织机构</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('location')" :disabled="markingEntity">
+            <span class="icon">📍</span>
+            <span>地点</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('date')" :disabled="markingEntity">
+            <span class="icon">📅</span>
+            <span>日期/时间</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('data')" :disabled="markingEntity">
+            <span class="icon">📊</span>
+            <span>数据/指标</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('concept')" :disabled="markingEntity">
+            <span class="icon">💡</span>
+            <span>概念/技术</span>
+          </div>
+          <div class="context-menu-item" @click.stop="markAsVariable('entity')" :disabled="markingEntity">
+            <span class="icon">🏷️</span>
+            <span>其他</span>
+          </div>
+        </div>
+      </div>
+      <div class="context-menu-item" @click="execContextAction('quote')">
+        <span class="icon">💬</span>
+        <span>引用到AI助手</span>
       </div>
+      
+      <!-- 加载状态 -->
       <div class="context-menu-loading" v-if="markingEntity">
         <el-icon class="is-loading"><Loading /></el-icon>
-        <span>标记中...</span>
+        <span>处理中...</span>
       </div>
     </div>
 
@@ -1440,6 +1483,7 @@ const variableForm = reactive({
 // 右键菜单
 const contextMenuVisible = ref(false)
 const contextMenuPos = reactive({ x: 0, y: 0 })
+const showEntitySubmenu = ref(false)
 const selectedText = ref('')
 const selectionRange = ref(null)
 
@@ -2492,6 +2536,42 @@ function handleContextMenu(event) {
   }
 }
 
+/**
+ * 执行右键菜单操作
+ */
+function execContextAction(action) {
+  switch (action) {
+    case 'copy':
+      document.execCommand('copy')
+      ElMessage.success('已复制')
+      break
+    case 'cut':
+      document.execCommand('cut')
+      ElMessage.success('已剪切')
+      break
+    case 'paste':
+      navigator.clipboard.readText().then(text => {
+        document.execCommand('insertText', false, text)
+      }).catch(() => {
+        ElMessage.warning('无法访问剪贴板')
+      })
+      break
+    case 'polish':
+      ElMessage.info('AI 润色功能开发中...')
+      break
+    case 'spell':
+      ElMessage.info('拼写检查功能开发中...')
+      break
+    case 'quote':
+      ElMessage.info('引用到AI助手功能开发中...')
+      break
+    default:
+      break
+  }
+  contextMenuVisible.value = false
+  showEntitySubmenu.value = false
+}
+
 /**
  * 获取选中文本所在的块信息
  */
@@ -4355,6 +4435,7 @@ onUnmounted(() => {
     cursor: pointer;
     transition: all 0.15s;
     color: var(--text-1);
+    position: relative;
 
     &:hover {
       background: var(--primary-light);
@@ -4370,6 +4451,7 @@ onUnmounted(() => {
       font-size: 14px;
       width: 20px;
       text-align: center;
+      flex-shrink: 0;
     }
     
     .shortcut {
@@ -4377,6 +4459,41 @@ onUnmounted(() => {
       font-size: 11px;
       color: var(--text-3);
     }
+    
+    .submenu-arrow {
+      margin-left: auto;
+      font-size: 14px;
+      color: var(--text-3);
+    }
+    
+    &.has-submenu {
+      &:hover .submenu-arrow {
+        color: var(--primary);
+      }
+    }
+  }
+  
+  // 子菜单
+  .context-submenu {
+    position: absolute;
+    left: 100%;
+    top: 0;
+    min-width: 150px;
+    background: var(--white);
+    border-radius: var(--radius-md);
+    box-shadow: var(--shadow-lg);
+    overflow: hidden;
+    
+    .context-menu-item {
+      padding: 8px 12px;
+      font-size: 12px;
+      gap: 8px;
+      
+      .icon {
+        font-size: 12px;
+        width: 16px;
+      }
+    }
   }
   
   .context-menu-divider {