3 次代码提交 f55703bf8d ... aa0f8f9bbc

作者 SHA1 备注 提交日期
  zq aa0f8f9bbc 预览时开启只读模式 1 周之前
  zq b9a80ec5a9 思维导图细节补充及预览节点点击 1 周之前
  dusenyao 65b21ae289 侧边栏 1 周之前

+ 86 - 5
src/components/CommonPreview.vue

@@ -49,6 +49,18 @@
         </ul>
         <p v-else style="text-align: center">暂无批注</p>
       </div>
+
+      <div class="sidebar">
+        <div
+          v-for="{ icon, title, handle } in sidebarIconList"
+          :key="icon"
+          :title="title"
+          class="sidebar-icon"
+          @click="handleSidebarClick(handle)"
+        >
+          <SvgIcon :icon-class="`sidebar-${icon}`" size="24" />
+        </div>
+      </div>
     </div>
 
     <el-dialog
@@ -57,7 +69,7 @@
       width="680px"
       :close-on-click-modal="false"
       class="audit-dialog"
-      @close="dialogClose"
+      @close="dialogClose('')"
     >
       <RichText
         v-model="remark_content"
@@ -73,6 +85,16 @@
         </el-button>
       </div>
     </el-dialog>
+
+    <el-dialog title="" :visible="visibleMindMap" width="1100px" class="audit-dialog" @close="dialogClose('MindMap')">
+      <MindMap
+        v-if="isChildDataLoad"
+        ref="mindMapRef"
+        :project-id="projectId"
+        :mind-map-json-data="mindMapJsonData"
+        @child-click="handleNodeClick"
+      />
+    </el-dialog>
   </div>
 </template>
 
@@ -81,6 +103,7 @@ import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview
 import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
 import RichText from '@/components/RichText.vue';
 import { isTrue } from '@/utils/common';
+import MindMap from '@/components/MindMap.vue';
 
 import {
   GetBookCoursewareInfo,
@@ -89,7 +112,12 @@ import {
   AddCoursewareAuditRemark,
   DeleteCoursewareAuditRemarkList,
 } from '@/api/project';
-import { ContentGetCoursewareContent_View, ChapterGetBookChapterStructExpandList, GetBookBaseInfo } from '@/api/book';
+import {
+  ContentGetCoursewareContent_View,
+  ChapterGetBookChapterStructExpandList,
+  GetBookBaseInfo,
+  MangerGetBookMindMap,
+} from '@/api/book';
 
 export default {
   name: 'CommonPreview',
@@ -97,6 +125,7 @@ export default {
     CoursewarePreview,
     MenuPopover,
     RichText,
+    MindMap,
   },
   props: {
     projectId: {
@@ -150,6 +179,7 @@ export default {
       remark_list: [],
       remark_list_obj: {}, // 存放以组件为对象的数组
       visible: false,
+
       remark_content: '',
       submit_loading: false,
       isTrue,
@@ -158,6 +188,20 @@ export default {
         y: -1,
         componentId: 'WHOLE',
       },
+      sidebarIconList: [
+        { icon: 'search', title: '搜索', handle: '' },
+        { icon: 'mindmap', title: '思维导图', handle: 'openMindMap' },
+        { icon: 'connect', title: '连接', handle: '' },
+        { icon: 'audio', title: '音频', handle: '' },
+        { icon: 'image', title: '图片', handle: '' },
+        { icon: 'video', title: '视频', handle: '' },
+        { icon: 'text', title: '文本', handle: '' },
+        { icon: 'collect', title: '收藏', handle: '' },
+        { icon: 'setting', title: '设置', handle: '' },
+      ],
+      visibleMindMap: false,
+      isChildDataLoad: false,
+      mindMapJsonData: {}, // 思维导图json数据
     };
   },
   created() {
@@ -268,8 +312,8 @@ export default {
         };
       }
     },
-    dialogClose() {
-      this.visible = false;
+    dialogClose(type) {
+      this[`visible${type}`] = false;
     },
     // 添加审校批注
     addCoursewareAuditRemark(id) {
@@ -313,6 +357,28 @@ export default {
         this.select_node,
       );
     },
+    /**
+     * 处理侧边栏图标点击事件
+     * @param {string} handle - 处理函数名
+     * @param {any} param - 处理函数参数
+     */
+    handleSidebarClick(handle, param) {
+      if (typeof handle === 'string' && handle && typeof this[handle] === 'function') {
+        this[handle](param);
+      }
+    },
+    openMindMap() {
+      MangerGetBookMindMap({ book_id: this.projectId }).then(({ content }) => {
+        if (content) {
+          this.mindMapJsonData = JSON.parse(content);
+          this.isChildDataLoad = true;
+        }
+      });
+      this.visibleMindMap = true;
+    },
+    handleNodeClick(data) {
+      console.log('子组件触发了事件', data); // 节点UID
+    },
   },
 };
 </script>
@@ -427,12 +493,12 @@ export default {
 
   .audit-content {
     display: flex;
-    column-gap: 20px;
     min-width: 1400px;
     height: calc(100vh - 175px);
 
     .remark-list {
       width: 300px;
+      margin-left: 20px;
       overflow: auto;
       border: 1px solid #e5e5e5;
 
@@ -477,6 +543,21 @@ export default {
         }
       }
     }
+
+    .sidebar {
+      display: flex;
+      flex-direction: column;
+      row-gap: 16px;
+      align-items: center;
+      height: 100%;
+      padding: 12px 8px;
+      margin-left: 8px;
+      box-shadow: -4px 0 4px rgba(0, 0, 0, 10%);
+
+      &-icon {
+        cursor: pointer;
+      }
+    }
   }
 }
 

+ 13 - 1
src/views/project_manage/project/components/MindMap.vue → src/components/MindMap.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="mind-map-container">
-    <div class="toolbar">
+    <div v-if="isEdit" class="toolbar">
       <button @click="addParentNode">添加父节点</button>
       <button @click="addNode">添加节点</button>
       <button @click="addChildNode">添加子节点</button>
@@ -28,6 +28,10 @@ MindMap.usePlugin(Export);
 export default {
   name: 'MindMap',
   props: {
+    isEdit: {
+      type: Boolean,
+      default: false,
+    },
     projectId: {
       type: String,
       required: true,
@@ -93,6 +97,7 @@ export default {
         //     fontSize: 14,
         //   },
         // },
+        readonly: !this.isEdit,
       });
       // this.mindMap.renderer.activeNodeList
       // 激活节点
@@ -106,6 +111,13 @@ export default {
       });
       this.mindMap.view.translateX(-400);
       this.mindMap.view.translateY(-50);
+      if (!this.isEdit) {
+        // 监听所有节点点击事件
+        this.mindMap.on('node_click', (node) => {
+          // console.log('点击的节点:', node?.nodeData?.data?.text || '文本');
+          this.$emit('child-click', `${node.uid}`);
+        });
+      }
     },
     destroyMindMap() {
       if (this.mindMap) {

+ 5 - 0
src/icons/svg/sibebar/sidebar-audio.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 24 24">
+  <path
+    d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z">
+  </path>
+</svg>

+ 5 - 0
src/icons/svg/sibebar/sidebar-collect.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 32 32">
+  <path
+    d="M4 24.667v-18c0-2.209 1.791-4 4-4v0h18.667c0.736 0 1.333 0.597 1.333 1.333v0 24c0 0.736-0.597 1.333-1.333 1.333v0h-18c-2.577 0-4.667-2.089-4.667-4.667v0zM25.333 26.667v-4h-16.667c-1.105 0-2 0.895-2 2s0.895 2 2 2v0h16.667zM13.333 5.333h-5.333c-0.736 0-1.333 0.597-1.333 1.333v0 13.783c0.583-0.284 1.269-0.449 1.994-0.449 0.002 0 0.004 0 0.007 0h16.666v-14.667h-2.667v10.667l-4.667-2.667-4.667 2.667v-10.667z">
+  </path>
+</svg>

+ 22 - 0
src/icons/svg/sibebar/sidebar-connect.svg

@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 48 48">
+  <rect width="48" height="48" fill="white" fill-opacity="0.01"></rect>
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M8 12C10.2091 12 12 10.2091 12 8C12 5.79086 10.2091 4 8 4C5.79086 4 4 5.79086 4 8C4 10.2091 5.79086 12 8 12Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M10 42C13.3137 42 16 39.3137 16 36C16 32.6863 13.3137 30 10 30C6.68629 30 4 32.6863 4 36C4 39.3137 6.68629 42 10 42Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M38 44C41.3137 44 44 41.3137 44 38C44 34.6863 41.3137 32 38 32C34.6863 32 32 34.6863 32 38C32 41.3137 34.6863 44 38 44Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M22 28C26.4183 28 30 24.4183 30 20C30 15.5817 26.4183 12 22 12C17.5817 12 14 15.5817 14 20C14 24.4183 17.5817 28 22 28Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M34 12C36.2091 12 38 10.2091 38 8C38 5.79086 36.2091 4 34 4C31.7909 4 30 5.79086 30 8C30 10.2091 31.7909 12 34 12Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M11 11L15 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M30 12L28 14" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M34 33.5L28 26" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M14 31L18 27" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+</svg>

+ 5 - 0
src/icons/svg/sibebar/sidebar-image.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 32 32">
+  <path
+    d="M6.437 28l-0.027 0.027-0.028-0.027h-2.393c-0.731-0.001-1.323-0.593-1.323-1.324v0-21.352c0.005-0.729 0.594-1.318 1.322-1.324h24.022c0.731 0 1.323 0.593 1.323 1.324v21.352c-0.005 0.729-0.594 1.318-1.322 1.324h-21.574zM26.667 20v-13.333h-21.333v18.667l13.333-13.333 8 8zM26.667 23.771l-8-8-9.563 9.563h17.563v-1.563zM10.667 14.667c-1.473 0-2.667-1.194-2.667-2.667s1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667s-1.194 2.667-2.667 2.667v0z">
+  </path>
+</svg>

+ 18 - 0
src/icons/svg/sibebar/sidebar-mindmap.svg

@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 48 48">
+  <path
+    d="M8 28C10.2091 28 12 26.2091 12 24C12 21.7909 10.2091 20 8 20C5.79086 20 4 21.7909 4 24C4 26.2091 5.79086 28 8 28Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"></path>
+  <path
+    d="M42 8C43.1046 8 44 7.10457 44 6C44 4.89543 43.1046 4 42 4C40.8954 4 40 4.89543 40 6C40 7.10457 40.8954 8 42 8Z"
+    stroke="#333" stroke-width="4" stroke-linejoin="round"></path>
+  <path
+    d="M42 26C43.1046 26 44 25.1046 44 24C44 22.8954 43.1046 22 42 22C40.8954 22 40 22.8954 40 24C40 25.1046 40.8954 26 42 26Z"
+    stroke="#333" stroke-width="4" stroke-linejoin="round"></path>
+  <path
+    d="M42 44C43.1046 44 44 43.1046 44 42C44 40.8954 43.1046 40 42 40C40.8954 40 40 40.8954 40 42C40 43.1046 40.8954 44 42 44Z"
+    stroke="#333" stroke-width="4" stroke-linejoin="round"></path>
+  <path d="M32 6H20V42H32" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"
+    fill="transparent">
+  </path>
+  <path d="M12 24H32" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+</svg>

+ 10 - 0
src/icons/svg/sibebar/sidebar-search.svg

@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 48 48">
+  <rect width="48" height="48" fill="white" fill-opacity="0.01"></rect>
+  <path
+    d="M21 38C30.3888 38 38 30.3888 38 21C38 11.6112 30.3888 4 21 4C11.6112 4 4 11.6112 4 21C4 30.3888 11.6112 38 21 38Z"
+    fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"></path>
+  <path d="M26.6568 14.3431C25.2091 12.8954 23.2091 12 21 12C18.7909 12 16.7909 12.8954 15.3431 14.3431" stroke="#333"
+    stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M33.2218 33.2218L41.7071 41.7071" stroke="#333" stroke-width="4" stroke-linecap="round"
+    stroke-linejoin="round"></path>
+</svg>

文件差异内容过多而无法显示
+ 2 - 0
src/icons/svg/sibebar/sidebar-setting.svg


+ 8 - 0
src/icons/svg/sibebar/sidebar-text.svg

@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 48 48">
+  <path d="M20.1429 42H8C6.89543 42 6 41.1046 6 40V7C6 5.89543 6.89543 5 8 5H40C41.1046 5 42 5.89543 42 7V16.7167"
+    stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="transparent"></path>
+  <path d="M17 14L29 14" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M23 28L23 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M27 38L37.5 23.5L42 27L31 42H27V38Z" fill="none" stroke="#333" stroke-width="4" stroke-linecap="round"
+    stroke-linejoin="round"></path>
+</svg>

+ 10 - 0
src/icons/svg/sibebar/sidebar-video.svg

@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon svg-icon-path-icon fill" viewBox="0 0 48 48">
+  <path
+    d="M39 6H9C7.34315 6 6 7.34315 6 9V39C6 40.6569 7.34315 42 9 42H39C40.6569 42 42 40.6569 42 39V9C42 7.34315 40.6569 6 39 6Z"
+    stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="transparent"></path>
+  <path d="M20.5 28V21.9378L25.75 24.9689L31 28L25.75 31.0311L20.5 34.0622V28Z" fill="none" stroke="#333"
+    stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M6 15H42" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M33 6L27 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+  <path d="M21 6L15 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
+</svg>

+ 39 - 1
src/views/book/courseware/create/components/CreateCanvas.vue

@@ -249,11 +249,49 @@ export default {
         spinner: 'el-icon-loading',
         background: 'rgba(0, 0, 0, 0.7)',
       });
+
+      /**
+       * 分组id列表
+       * 将 data.row_list 中的每一行转换为一个组
+       */
+      const group_component_id = this.data.row_list.flatMap((row, index) => {
+        // 查找每行中第一个包含 describe、label 或 stem 的组件
+        let findKey = '';
+        let findType = '';
+        row.col_list.some((col) => {
+          const findItem = col.grid_list.find(({ type }) => {
+            return ['describe', 'label', 'stem'].includes(type);
+          });
+          if (findItem) {
+            findKey = findItem.id;
+            findType = findItem.type;
+            return true;
+          }
+        });
+        let name = `组${index + 1}`;
+
+        // 如果有标签类组件,获取对应名称
+        if (findKey) {
+          let item = this.findChildComponentByKey(`grid-${findKey}`);
+          if (['describe', 'stem'].includes(findType)) {
+            name = item.data.content.replace(/<[^>]+>/g, ''); // 去掉html标签
+          } else if (findType === 'label') {
+            name = item.data.dynamicTags.map((tag) => tag.text).join(', ');
+          }
+        }
+
+        return {
+          name,
+          id_list: row.col_list.map((col) => col.grid_list.map((grid) => grid.id)),
+        };
+      });
+
       ContentSaveCoursewareContent({
         id: this.courseware_id,
         category: 'NEW',
         content: JSON.stringify(this.data),
         component_id_list,
+        group_component_id,
       }).then(() => {
         this.$message.success('保存成功');
         loading.close();
@@ -931,7 +969,7 @@ export default {
 
     // 获取子组件
     findChildComponentByKey(key) {
-      return this.$children.find((child) => child.$vnode.key === key);
+      return this.$refs.component.find((child) => child.$vnode.key === key);
     },
   },
 };

+ 8 - 9
src/views/book/courseware/create/components/base/audio/Audio.vue

@@ -47,15 +47,14 @@ export default {
     },
     // 思维导图
     handlerMindMap() {
-      this.$set(this.data.mind_map, 'node_list', [
-        {
-          name: '音频',
-          node_list:
-            this.data.file_list?.map((file) => ({
-              name: file.file_name === undefined ? '' : file.file_name,
-            })) ?? [],
-        },
-      ]);
+      this.$set(
+        this.data.mind_map,
+        'node_list',
+        this.data.file_list?.map((file) => ({
+          name: file.file_name === undefined ? '' : file.file_name,
+          id: Math.random().toString(36).substring(2, 12),
+        })) ?? [],
+      );
     },
   },
 };

+ 2 - 1
src/views/book/courseware/create/components/base/describe/Describe.vue

@@ -97,7 +97,8 @@ export default {
     handlerMindMap() {
       this.$set(this.data.mind_map, 'node_list', [
         {
-          name: `题干:${this.sanitizeHTML(this.data.content)}`,
+          name: `${this.sanitizeHTML(this.data.content)}`,
+          id: Math.random().toString(36).substring(2, 12),
         },
       ]);
     },

+ 8 - 9
src/views/book/courseware/create/components/base/picture/Picture.vue

@@ -47,15 +47,14 @@ export default {
     },
     // 思维导图
     handlerMindMap() {
-      this.$set(this.data.mind_map, 'node_list', [
-        {
-          name: '图片',
-          node_list:
-            this.data.file_list?.map((file) => ({
-              name: file.file_name === undefined ? '' : file.file_name,
-            })) ?? [],
-        },
-      ]);
+      this.$set(
+        this.data.mind_map,
+        'node_list',
+        this.data.file_list?.map((file) => ({
+          name: file.file_name === undefined ? '' : file.file_name,
+          id: Math.random().toString(36).substring(2, 12),
+        })) ?? [],
+      );
     },
   },
 };

+ 2 - 1
src/views/book/courseware/create/components/base/stem/Stem.vue

@@ -98,7 +98,8 @@ export default {
     handlerMindMap() {
       this.$set(this.data.mind_map, 'node_list', [
         {
-          name: `描述:${this.sanitizeHTML(this.data.content)}`,
+          name: `${this.sanitizeHTML(this.data.content)}`,
+          id: Math.random().toString(36).substring(2, 12),
         },
       ]);
       // console.log(this.data.mind_map);

+ 8 - 9
src/views/book/courseware/create/components/base/video/Video.vue

@@ -47,15 +47,14 @@ export default {
     },
     // 思维导图
     handlerMindMap() {
-      this.$set(this.data.mind_map, 'node_list', [
-        {
-          name: '视频',
-          node_list:
-            this.data.file_list?.map((file) => ({
-              name: file.file_name === undefined ? '' : file.file_name,
-            })) ?? [],
-        },
-      ]);
+      this.$set(
+        this.data.mind_map,
+        'node_list',
+        this.data.file_list?.map((file) => ({
+          name: file.file_name === undefined ? '' : file.file_name,
+          id: Math.random().toString(36).substring(2, 12),
+        })) ?? [],
+      );
     },
   },
 };

+ 8 - 2
src/views/project_manage/project/ProjectMindMap.vue

@@ -12,13 +12,19 @@
       </div>
     </div>
     <p v-if="isWaiting" class="waiting">数据生成中...请等待</p>
-    <MindMap v-if="isChildDataLoad" ref="mindMapRef" :project-id="project_id" :mind-map-json-data="mindMapJsonData" />
+    <MindMap
+      v-if="isChildDataLoad"
+      ref="mindMapRef"
+      :is-edit="true"
+      :project-id="project_id"
+      :mind-map-json-data="mindMapJsonData"
+    />
   </div>
 </template>
 
 <script>
 import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
-import MindMap from './components/MindMap.vue';
+import MindMap from '@/components/MindMap.vue';
 import { GetProjectBaseInfo } from '@/api/project';
 import { MangerGetBookMindMap, MangerGenerateMindMapByBookContent, MangerSaveBookMindMap } from '@/api/book';
 

部分文件因为文件数量过多而无法显示