lsc 12 hours ago
parent
commit
46e1d1a2df
2 changed files with 234 additions and 82 deletions
  1. 233 81
      src/components/CollapsibleToolbar/index.vue
  2. 1 1
      src/views/Editor/index.vue

+ 233 - 81
src/components/CollapsibleToolbar/index.vue

@@ -1,22 +1,11 @@
 <template>
   <div class="collapsible-toolbar" :class="{ collapsed: isCollapsed }">
-    <!-- <div class="toolbar-header" @click="toggleCollapse">
-      <div class="header-content">
-        <slot name="header">
-          <span class="default-title">工具箱</span>
-        </slot>
-        <div class="collapse-icon">
-          <IconRight v-if="isCollapsed" />
-          <IconLeft v-else />
-        </div>
-      </div>
-    </div> -->
     <div class="toolbar-content" v-show="!isCollapsed">
       <div class="sidebar-content">
         <div class="sidebar-item" :class="{ active: activeSubmenu === 'interactive' }" @click="toggleSubmenu('interactive')">
           <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
             <circle cx="12" cy="12" r="3"/>
-            <path d="M12 1v6m0 6v6M5.64 5.64l4.24 4.24m4. 4.24l4.24 4.24M1 12h6m6 0h6M5.64 18.36l4.24-4.24m4.24-4.24l4.24-4.24"/>
+            <path d="M12 1v6m0 6v6M5.64 5.64l4.24 4.24m4. 4.24l4.24 4.24M1 12h6m6 0h6M5.64 18.36l4.24-4.24m4.24-4.24l4kt-4.24"/>
           </svg>
           <span class="item-label">互动工具</span>
         </div>
@@ -52,15 +41,23 @@
           </svg>
           <span class="item-label">创作空间</span>
         </div>
+        <div class="sidebar-item" :class="{ active: activeSubmenu === 'contentlist' }" @click="toggleSubmenu('contentlist')">
+          <svg class="item-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="item-label">内容列表</span>
+        </div>
       </div>
     </div>
+    
     <div class="submenu" :class="{ visible: activeSubmenu === 'interactive' }">
       <div class="submenu-item" @click="handleToolClick('choice')">
         <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
           <circle cx="12" cy="12" r="10"/>
           <path d="M12 16v-4m0-4h.01"/>
         </svg>
-        <span class="submenu-label">选择题工具</span>
+        <span class="submenu-label">选择题</span>
       </div>
       <div class="submenu-item" @click="handleToolClick('qa')">
         <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -69,11 +66,41 @@
         <span class="submenu-label">问答</span>
       </div>
     </div>
+    
+    <div class="content-list-submenu" :class="{ visible: activeSubmenu === 'contentlist' }">
+      <div v-if="contentList.length === 0" class="empty-state">
+        <div class="empty-icon">📚</div>
+        <div class="empty-title">暂无学习内容</div>
+        <div class="empty-title">请先上传或创建学习内容</div>
+      </div>
+      <div v-else class="content-list">
+        <div 
+          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>
+      </div>
+    </div>
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue'
+import useCreateElement from '@/hooks/useCreateElement'
+
+interface ContentItem {
+  tool?: number
+  title?: string
+  url?: string
+}
 
 const props = withDefaults(defineProps<{
   defaultCollapsed?: boolean
@@ -87,6 +114,9 @@ const emit = defineEmits<{
 
 const isCollapsed = ref(props.defaultCollapsed)
 const activeSubmenu = ref<string | null>(null)
+const contentList = ref<ContentItem[]>([])
+
+const { createFrameElement } = useCreateElement()
 
 const toggleCollapse = () => {
   isCollapsed.value = !isCollapsed.value
@@ -99,6 +129,9 @@ const toggleSubmenu = (menu: string) => {
   }
   else {
     activeSubmenu.value = menu
+    if (menu === 'contentlist') {
+      loadContentList()
+    }
   }
 }
 
@@ -129,75 +162,61 @@ const handleToolClick = (tool: string) => {
     parentWindow?.addTool?.(15)
   }
 }
-</script>
 
-<style lang="scss" scoped>
-.collapsible-toolbar {
-  width: 100%;
-  height: 100%;
-  display: flex;
-  // flex-direction: column;
-  background: #fff;
-  border-right: 1px solid #e5e7eb;
-  transition: width 0.3s ease;
-}
-
-.toolbar-header {
-  height: 48px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 0 16px;
-  border-bottom: 1px solid #f0f0f0;
-  cursor: pointer;
-  user-select: none;
-  background: #f9fafb;
-  transition: all 0.3s ease;
-
-  &:hover {
-    background: #f3f4f6;
+const loadContentList = () => {
+  try {
+    interface ParentWindowWithToolList extends Window {
+      pptToolList?: ContentItem[]
+    }
+    const parentWindow = window.parent as ParentWindowWithToolList
+    contentList.value = parentWindow?.pptToolList || []
+  }
+  catch (error) {
+    console.error('加载内容列表失败:', error)
+    contentList.value = []
   }
 }
 
-.collapsed .toolbar-header {
-  padding: 0;
-  justify-content: center;
-}
-
-.header-content {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  font-size: 14px;
-  font-weight: 500;
-  color: #111827;
-  transition: all 0.3s ease;
-}
-
-.collapsed .header-content {
-  gap: 0;
+const insertContent = (item: ContentItem) => {
+  if (!item.tool || !item.url) return
+  createFrameElement(item.url, item.tool)
 }
 
-.default-title {
-  flex: 1;
+const getTypeLabel = (type?: number) => {
+  const typeMap: Record<number, string> = {
+    45: '选择题',
+    15: '问答题',
+    72: 'AI应用',
+    73: 'H5页面',
+    74: '视频',
+    75: 'B站视频',
+    76: '创作空间'
+  }
+  return typeMap[type || 0] || '未知'
 }
 
-.collapsed .default-title {
-  display: none;
+const getTypeClass = (type?: number) => {
+  const classMap: Record<number, string> = {
+    45: 'type-choice',
+    15: 'type-question',
+    72: 'type-ai',
+    73: 'type-h5',
+    74: 'type-video',
+    75: 'type-bilibili',
+    76: 'type-app-center'
+  }
+  return classMap[type || 0] || 'type-default'
 }
+</script>
 
-.collapse-icon {
-  width: 20px;
-  height: 20px;
+<style lang="scss" scoped>
+.collapsible-toolbar {
+  width: 100%;
+  height: 100%;
   display: flex;
-  align-items: center;
-  justify-content: center;
-  color: #6b7280;
-  transition: transform 0.3s ease;
-}
-
-.collapsed .collapse-icon {
-  transform: rotate(180deg);
+  background: #fff;
+  border-right: 1px solid #e5e7eb;
+  transition: width 0.3s ease;
 }
 
 .toolbar-content {
@@ -214,12 +233,6 @@ const handleToolClick = (tool: string) => {
   position: relative;
 }
 
-.sidebar {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-
 .sidebar-item {
   width: 84px;
   padding: 12px 8px;
@@ -286,16 +299,12 @@ const handleToolClick = (tool: string) => {
 }
 
 .submenu {
-  // position: absolute;
-  // left: 100%;
-  // top: 0;
   width: 0;
   min-width: 0;
   overflow: hidden;
   transition: all 0.3s ease;
   background: #fff;
   border-radius: 12px;
-  // box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
   z-index: 100;
 
   &.visible {
@@ -346,4 +355,147 @@ const handleToolClick = (tool: string) => {
 .submenu-item:hover .submenu-label {
   color: #285cf5;
 }
+
+.content-list-submenu {
+  width: 0;
+  min-width: 0;
+  overflow: hidden;
+  transition: all 0.3s ease;
+  background: #fff;
+  border-radius: 12px;
+  z-index: 100;
+
+  &.visible {
+    width: 300px;
+    min-width: 300px;
+    padding: 8px 0;
+  }
+}
+
+.empty-state {
+  text-align: center;
+  padding: 20px 10px;
+  color: #666;
+}
+
+.empty-icon {
+  font-size: 32px;
+  margin-bottom: 8px;
+}
+
+.empty-title {
+  font-size: 12px;
+  color: #999;
+}
+
+.content-list {
+  max-height: 400px;
+  overflow-y: auto;
+  padding: 4px 0;
+
+  &::-webkit-scrollbar {
+    width: 4px;
+  }
+
+  &::-webkit-scrollbar-track {
+    background: #f1f1f1;
+    border-radius: 2px;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background: #c1c1c1;
+    border-radius: 2px;
+
+    &:hover {
+      background: #a8a8a8;
+    }
+  }
+}
+
+.content-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 8px 12px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  border-radius: 8px;
+  margin: 2px 8px;
+  position: relative;
+
+  &:hover {
+    background-color: #f3f4f6;
+    color: #285cf5;
+  }
+
+  &:active {
+    background-color: #e5e7eb;
+  }
+}
+
+.content-icon {
+  width: 16px;
+  height: 16px;
+  flex-shrink: 0;
+  color: #9ca3af;
+}
+
+.content-item:hover .content-icon {
+  color: #285cf5;
+}
+
+.content-label {
+  flex: 1;
+  font-size: 13px;
+  color: #6b7280;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.content-item:hover .content-label {
+  color: #285cf5;
+}
+
+.type-tag {
+  padding: 2px 6px;
+  border-radius: 4px;
+  font-size: 10px;
+  font-weight: 500;
+  color: #fff;
+  white-space: nowrap;
+  flex-shrink: 0;
+
+  &.type-choice {
+    background-color: #4caf50;
+  }
+
+  &.type-question {
+    background-color: #ff9800;
+  }
+
+  &.type-ai {
+    background-color: #2196f3;
+  }
+
+  &.type-h5 {
+    background-color: #9c27b0;
+  }
+
+  &.type-video {
+    background-color: #f44336;
+  }
+
+  &.type-bilibili {
+    background-color: #fb7299;
+  }
+
+  &.type-default {
+    background-color: #757575;
+  }
+
+  &.type-app-center {
+    background-color: #673ab7;
+  }
+}
 </style>

+ 1 - 1
src/views/Editor/index.vue

@@ -13,7 +13,7 @@
            v-show="false"
         />
       </div>
-      <Toolbar class="layout-content-right" v-show="false"/>
+      <Toolbar class="layout-content-right"/>
     </div>
   </div>