Browse Source

feat(CollapsibleToolbar): 添加内容项操作按钮并优化样式

- 为内容项添加预览、编辑、复制和删除操作按钮
- 调整内容项布局和交互样式
- 增加内容项悬停效果和操作按钮样式
- 扩展ContentItem接口添加id字段
lsc 7 months ago
parent
commit
2beed1716d
1 changed files with 127 additions and 23 deletions
  1. 127 23
      src/components/CollapsibleToolbar/index.vue

+ 127 - 23
src/components/CollapsibleToolbar/index.vue

@@ -78,17 +78,47 @@
           v-for="(item, index) in contentList" 
           :key="index"
           class="content-item"
-          @click="insertContent(item)"
         >
-          <svg class="content-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-            <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
-            <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
-          </svg>
-          <span class="content-label">{{ item.title }}</span>
-          <span class="type-tag" :class="getTypeClass(item.tool)">{{ getTypeLabel(item.tool) }}</span>
+          <div class="content-main" @click="insertContent(item)">
+            <svg class="content-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
+              <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
+            </svg>
+            <span class="content-label">{{ item.title }}</span>
+            <span class="type-tag" :class="getTypeClass(item.tool)">{{ getTypeLabel(item.tool) }}</span>
+          </div>
+          <div class="content-actions">
+            <template v-if="item.tool === 74 || item.tool === 75">
+              <div class="action-btn" @click.stop="previewVideo(item)" title="预览">
+                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <polygon points="5 3 19 12 5 21 5 3"/>
+                </svg>
+              </div>
+            </template>
+            <template v-if="item.tool !== 76">
+              <div class="action-btn" @click.stop="editContent(item)" title="编辑">
+                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
+                  <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
+                </svg>
+              </div>
+              <div class="action-btn" @click.stop="copyContent(item)" title="复制">
+                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
+                  <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
+                </svg>
+              </div>
+            </template>
+            <div class="action-btn delete" @click.stop="deleteContent(item)" title="删除">
+              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <polyline points="3 6 5 6 21 6"/>
+                <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
+              </svg>
+            </div>
+          </div>
         </div>
       </div>
-    </div>
+      div>
   </div>
 </template>
 
@@ -100,6 +130,7 @@ interface ContentItem {
   tool?: number
   title?: string
   url?: string
+  id?: string
 }
 
 const props = withDefaults(defineProps<{
@@ -135,8 +166,6 @@ const toggleSubmenu = (menu: string) => {
   }
 }
 
-
-
 const handleToolClick = (tool: string) => {
   interface ParentWindowWithToolList extends Window {
     addTool?: (id: number) => void;
@@ -181,13 +210,43 @@ const loadContentList = () => {
 
 (window as any).loadContentList = loadContentList
 
-
-
 const insertContent = (item: ContentItem) => {
   if (!item.tool || !item.url) return
   createFrameElement(item.url, item.tool)
 }
 
+const previewVideo = (item: ContentItem) => {
+  interface ParentWindowWithToolList extends Window {
+    previewVideo?: (item: ContentItem) => void;
+  }
+  const parentWindow = window.parent as ParentWindowWithToolList
+  parentWindow?.previewVideo?.(item)
+}
+
+const editContent = (item: ContentItem) => {
+  interface ParentWindowWithToolList extends Window {
+    toolBtn?: (action: number, id: string) => void;
+  }
+  const parentWindow = window.parent as ParentWindowWithToolList
+  parentWindow?.toolBtn?.(0, item.id || '')
+}
+
+const copyContent = (item: ContentItem) => {
+  interface ParentWindowWithToolList extends Window {
+    toolBtn?: (action: number, id: string) => void;
+  }
+  const parentWindow = window.parent as ParentWindowWithToolList
+  parentWindow?.toolBtn?.(1, item.id || '')
+}
+
+const deleteContent = (item: ContentItem) => {
+  interface ParentWindowWithToolList extends Window {
+    toolBtn?: (action: number, id: string) => void;
+  }
+  const parentWindow = window.parent as ParentWindowWithToolList
+  parentWindow?.toolBtn?.(2, item.id || '')
+}
+
 const getTypeLabel = (type?: number) => {
   const typeMap: Record<number, string> = {
     45: '选择题',
@@ -372,8 +431,8 @@ const getTypeClass = (type?: number) => {
   z-index: 100;
 
   &.visible {
-    width: 300px;
-    min-width: 300px;
+    width: 380px;
+    min-width: 380px;
     padding: 8px 0;
   }
 }
@@ -421,21 +480,31 @@ const getTypeClass = (type?: number) => {
 .content-item {
   display: flex;
   align-items: center;
-  gap: 8px;
+  justify-content: space-between;
   padding: 8px 12px;
-  cursor: pointer;
-  transition: all 0.2s ease;
   border-radius: 8px;
   margin: 2px 8px;
   position: relative;
+  transition: all 0.2s ease;
 
   &:hover {
-    background-color: #f3f4f6;
-    color: #285cf5;
+    background-color: #f8f9fa;
   }
+}
 
-  &:active {
-    background-color: #e5e7eb;
+.content-main {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex: 1;
+  min-width: 0;
+  cursor: pointer;
+  padding: 4px;
+  border-radius: 6px;
+  transition: all 0.2s ease;
+
+  &:hover {
+    background-color: #f3f4f6;
   }
 }
 
@@ -446,7 +515,7 @@ const getTypeClass = (type?: number) => {
   color: #9ca3af;
 }
 
-.content-item:hover .content-icon {
+.content-main:hover .content-icon {
   color: #285cf5;
 }
 
@@ -459,10 +528,45 @@ const getTypeClass = (type?: number) => {
   text-overflow: ellipsis;
 }
 
-.content-item:hover .content-label {
+.content-main:hover .content-label {
   color: #285cf5;
 }
 
+.content-actions {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.action-btn {
+  width: 28px;
+  height: 28px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  color: #9ca3af;
+
+  svg {
+    width: 14px;
+    height: 14px;
+  }
+
+  &:hover {
+    background-color: #f3f4f6;
+    color: #285cf5;
+  }
+
+  &.delete {
+    &:hover {
+      background-color: #fee2e2;
+      color: #ef4444;
+    }
+  }
+}
+
 .type-tag {
   padding: 2px 6px;
   border-radius: 4px;