Przeglądaj źródła

保存为个人模板、使用模板

dsy 2 miesięcy temu
rodzic
commit
f479981a68

+ 8 - 8
src/api/book.js

@@ -285,50 +285,50 @@ export function GetBookUnifiedAttrib(data) {
 
 /**
  *@description 添加我的笔记
- * @param {object} data  
+ * @param {object} data
  */
 export function AddMyNote(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-AddMyNote`, data);
 }
 /**
  *@description 更新我的笔记
- * @param {object} data  
+ * @param {object} data
  */
 export function UpdateMyNote(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-UpdateMyNote`, data);
 }
 /**
  * @description 得到我的笔记列表
- * @param {object} data 
+ * @param {object} data
  */
 export function GetMyNoteList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-GetMyNoteList`, data);
 }
 /**
  * @description 删除我的笔记
- * @param {object} data 
+ * @param {object} data
  */
 export function DeleteMyNote(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-DeleteMyNote`, data);
 }
 /**
  *@description 添加我的收藏
- * @param {object} data  
+ * @param {object} data
  */
 export function AddMyCollect(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-AddMyCollect`, data);
 }
 /**
  * @description 得到我的收藏列表
- * @param {object} data 
+ * @param {object} data
  */
 export function GetMyCollectList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-GetMyCollectList`, data);
 }
 /**
  * @description 删除我的收藏
- * @param {object} data 
+ * @param {object} data
  */
 export function DeleteMyCollect(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-DeleteMyCollect`, data);
-}
+}

+ 11 - 0
src/api/list.js

@@ -74,3 +74,14 @@ export function PageQueryProjectResourceList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryProjectResourceList`, data);
 }
 
+/**
+ * @description 分页查询模板列表(用于编辑教材时选择模板)
+ * @param {object} data
+ * @param {number} data.page_capacity - 每页容量
+ * @param {number} data.cur_page - 当前查询页码
+ * @param {number} data.storage_type - 存储类型 0【个人库】1【机构库】
+ * @param {string} data.name - 名称,空表示查询所有
+ */
+export function PageQueryTemplateList_EditBookSelect(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryTemplateList_EditBookSelect`, data);
+}

+ 13 - 0
src/api/template.js

@@ -0,0 +1,13 @@
+import { http } from '@/utils/http';
+
+/**
+ * @description 把课件保存为个人模板
+ * @param {object} data
+ * @param {string} data.courseware_id 课件ID
+ */
+export function SaveCoursewareAsTemplatePersonal(data) {
+  return http.post(
+    `${process.env.VUE_APP_EepServer}?MethodName=template_manager-SaveCoursewareAsTemplate_Personal`,
+    data,
+  );
+}

+ 12 - 0
src/views/book/courseware/create/components/CreateCanvas.vue

@@ -1309,6 +1309,18 @@ export default {
       });
       return data;
     },
+    /**
+     * 加载模板数据
+     * @param {Object} param
+     * @param {Object} param.data - 模板数据
+     * @param {Array} param.content_group_row_list - 内容分组行列表
+     */
+    loadTemplateData_CreateCanvas({ data, content_group_row_list }) {
+      this.data = data || {
+        row_list: [],
+      };
+      this.content_group_row_list = content_group_row_list || [];
+    },
   },
 };
 </script>

+ 4 - 2
src/views/book/courseware/create/components/common/ModuleMixin.js

@@ -74,7 +74,7 @@ const mixin = {
       immediate: true,
     },
   },
-  inject: ['courseware_id', 'project_id', 'getBookUnifiedAttr'],
+  inject: ['courseware_id', 'project_id', 'getBookUnifiedAttr', 'getTemporaryCoursewareID'],
   created() {
     if (this.type !== 'interaction') {
       this.GetCoursewareComponentContent();
@@ -91,7 +91,9 @@ const mixin = {
   methods: {
     GetCoursewareComponentContent() {
       if (this.courseware_id === '' || this.id === '') return;
-      ContentGetCoursewareComponentContent({ courseware_id: this.courseware_id, component_id: this.id }).then(
+      let temporaryCoursewareID = this.getTemporaryCoursewareID();
+      let coursewareID = temporaryCoursewareID || this.courseware_id;
+      ContentGetCoursewareComponentContent({ courseware_id: coursewareID, component_id: this.id }).then(
         ({ content }) => {
           if (content) {
             this.data = JSON.parse(content);

+ 9 - 0
src/views/book/courseware/create/index.vue

@@ -178,6 +178,15 @@ export default {
         middle.scrollTo({ top: 0, behavior: 'smooth' });
       }
     },
+    /**
+     * 加载模板数据
+     * @param {Object} param
+     * @param {Object} param.data - 模板数据
+     * @param {Array} param.content_group_row_list - 内容分组行列表
+     */
+    loadTemplateData_Create({ data, content_group_row_list }) {
+      this.$refs.createCanvas.loadTemplateData_CreateCanvas({ data, content_group_row_list });
+    },
   },
 };
 </script>

+ 2 - 4
src/views/book/courseware/data/uploadPreview.js

@@ -28,11 +28,9 @@ export function getUploadPreviewData() {
     file_info_list: [],
     file_id_list: [], // 文件 id['20032-121212', '20032-121216']
     file_info: {}, // 存放文件序号
-    file_list: [],// 内容中包含的文件列表,
+    file_list: [], // 内容中包含的文件列表,
     mind_map: {
-      node_list: [
-        { name: '上传组件' }
-      ], // 思维导图数据
+      node_list: [{ name: '上传组件' }], // 思维导图数据
     },
     multilingual: [], // 多语言
   };

+ 8 - 2
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -694,12 +694,18 @@ export default {
 
       return null;
     },
+    /**
+     * 滚动到指定data-id的元素
+     * @param {string} dataId 元素的data-id属性值
+     * @param {number} offset 偏移量
+     */
     scrollToDataId(dataId, offset) {
-      if (!offset) offset = 0;
+      let _offset = offset;
+      if (!_offset) _offset = 0;
       const element = document.querySelector(`div[data-id="${dataId}"]`);
       if (element) {
         const elementPosition = element.getBoundingClientRect().top + window.pageYOffset;
-        const offsetPosition = elementPosition - offset;
+        const offsetPosition = elementPosition - _offset;
 
         element.scrollIntoView({
           behavior: 'smooth', // 滚动行为:'auto' | 'smooth'

+ 110 - 110
src/views/book/courseware/preview/components/article/NormalModelChs.vue

@@ -100,6 +100,13 @@
                               pItem.className ? pItem.className : '',
                               noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                             ]"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -109,13 +116,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
                           >
                           <span
@@ -196,6 +196,13 @@
                               pItem.className ? pItem.className : '',
                               noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                             ]"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -205,13 +212,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
                           >
                         </span>
@@ -223,6 +223,13 @@
                               noFont.indexOf(item.wordsList[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -232,13 +239,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{
                               NumberList.indexOf(item.wordsList[pIndex + 1].pinyin) == -1
                                 ? item.wordsList[pIndex + 1].pinyin
@@ -312,6 +312,13 @@
                               noFont.indexOf(item.wordsList[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -321,13 +328,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{
                               NumberList.indexOf(item.wordsList[pIndex + 1].pinyin) == -1
                                 ? item.wordsList[pIndex + 1].pinyin
@@ -351,6 +351,13 @@
                               noFont.indexOf(item.wordsList[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -360,13 +367,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{
                               NumberList.indexOf(item.wordsList[pIndex + 2].pinyin) == -1
                                 ? item.wordsList[pIndex + 2].pinyin
@@ -440,6 +440,13 @@
                               noFont.indexOf(item.wordsList[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -449,13 +456,6 @@
                                 pItem.articleSentIndex,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{
                               NumberList.indexOf(item.wordsList[pIndex + 2].pinyin) == -1
                                 ? item.wordsList[pIndex + 2].pinyin
@@ -473,6 +473,11 @@
                             pItem.className ? pItem.className : '',
                             noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                           ]"
+                          :style="{
+                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                            height:
+                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
+                          }"
                           @click.stop="
                             viewNotes(
                               $event,
@@ -482,11 +487,6 @@
                               pItem.articleSentIndex,
                             )
                           "
-                          :style="{
-                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                            height:
-                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
-                          }"
                           >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
                         >
                         <span
@@ -566,6 +566,11 @@
                             pItem.className ? pItem.className : '',
                             noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                           ]"
+                          :style="{
+                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                            height:
+                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
+                          }"
                           @click.stop="
                             viewNotes(
                               $event,
@@ -575,11 +580,6 @@
                               pItem.articleSentIndex,
                             )
                           "
-                          :style="{
-                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                            height:
-                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
-                          }"
                           >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
                         >
                       </template>
@@ -667,6 +667,13 @@
                               sentIndex == index ? 'wordBlank' : '',
                               noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                             ]"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -676,13 +683,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ pItem.pinyin }}</span
                           >
                         </template>
@@ -737,6 +737,13 @@
                               sentIndex == index ? 'wordBlank' : '',
                               noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                             ]"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -746,13 +753,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ pItem.pinyin }}</span
                           >
                         </template>
@@ -767,6 +767,13 @@
                               noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -776,13 +783,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ item.sentArr[pIndex + 1].pinyin }}</span
                           >
                         </template>
@@ -843,6 +843,13 @@
                               noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -852,13 +859,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ item.sentArr[pIndex + 1].pinyin }}</span
                           >
                         </template>
@@ -880,6 +880,13 @@
                               noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -889,13 +896,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ item.sentArr[pIndex + 2].pinyin }}</span
                           >
                         </template>
@@ -946,6 +946,13 @@
                               noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
                             ]"
                             style="text-align: left"
+                            :style="{
+                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                              height:
+                                attrib && attrib.pinyin_size
+                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
+                                  : '22px',
+                            }"
                             @click.stop="
                               viewNotes(
                                 $event,
@@ -955,13 +962,6 @@
                                 index,
                               )
                             "
-                            :style="{
-                              fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                              height:
-                                attrib && attrib.pinyin_size
-                                  ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
-                                  : '22px',
-                            }"
                             >{{ item.sentArr[pIndex + 2].pinyin }}</span
                           >
                         </template>
@@ -978,6 +978,11 @@
                             sentIndex == index ? 'wordBlank' : '',
                             noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                           ]"
+                          :style="{
+                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                            height:
+                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
+                          }"
                           @click.stop="
                             viewNotes(
                               $event,
@@ -987,11 +992,6 @@
                               index,
                             )
                           "
-                          :style="{
-                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                            height:
-                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
-                          }"
                           >{{ pItem.pinyin }}</span
                         >
                       </template>
@@ -1054,6 +1054,11 @@
                             sentIndex == index ? 'wordBlank' : '',
                             noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
                           ]"
+                          :style="{
+                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
+                            height:
+                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
+                          }"
                           @click.stop="
                             viewNotes(
                               $event,
@@ -1063,11 +1068,6 @@
                               index,
                             )
                           "
-                          :style="{
-                            fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
-                            height:
-                              attrib && attrib.pinyin_size ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt' : '22px',
-                          }"
                           >{{ pItem.pinyin }}</span
                         >
                       </template>
@@ -1182,7 +1182,7 @@
           width: isMobile ? '100%' : '',
         }"
       >
-        <Notecard :item="curNoteCon" :change-card="changeCard" :attrib="attrib" :isMobile="isMobile" />
+        <Notecard :item="curNoteCon" :change-card="changeCard" :attrib="attrib" :is-mobile="isMobile" />
       </div>
     </template>
   </div>
@@ -1355,8 +1355,8 @@ export default {
         dItem.wordsList.forEach((sItem, sIndex) => {
           let sentArr = [];
           sItem.forEach((wItem, wIndex) => {
-            let startIndex = wIndex == 0 ? 0 : sentArr[wIndex - 1].startIndex + sentArr[wIndex - 1].chs.length;
-            let endIndex = wIndex == 0 ? wItem.chs.length : sentArr[wIndex - 1].endIndex + wItem.chs.length;
+            let startIndex = wIndex === 0 ? 0 : sentArr[wIndex - 1].startIndex + sentArr[wIndex - 1].chs.length;
+            let endIndex = wIndex === 0 ? wItem.chs.length : sentArr[wIndex - 1].endIndex + wItem.chs.length;
             this.mergeWordSymbol(wItem);
             let obj = {
               paraIndex: dIndex, // 段落索引
@@ -1386,14 +1386,14 @@ export default {
             }
             sentArr.push(obj);
             paraArr.push(obj);
-            if (wIndex == sItem.length - 1) {
-              asIndex++;
+            if (wIndex === sItem.length - 1) {
+              asIndex += 1;
             }
             if (wItem.pinyin) dhaspinyin = true;
           });
         });
         let curSentencesLeg = dItem.sentences.length;
-        let startLeg = dIndex == 0 ? 0 : curQue.detail[dIndex - 1].endLeg;
+        let startLeg = dIndex === 0 ? 0 : curQue.detail[dIndex - 1].endLeg;
         let endLeg = startLeg + curSentencesLeg;
         dItem.endLeg = endLeg;
         let timeList = curQue.wordTime ? curQue.wordTime.slice(startLeg, endLeg) : [];

+ 249 - 0
src/views/personal_workbench/edit_task/edit/UseTemplate.vue

@@ -0,0 +1,249 @@
+<template>
+  <el-dialog
+    custom-class="use-template-dialog"
+    :visible="visible"
+    :close-on-click-modal="false"
+    width="1660px"
+    @close="dialogClose"
+  >
+    <header class="dialog-header">
+      <div class="title">
+        <span>选择模板</span>
+      </div>
+      <div class="operator">
+        <span class="link" @click="useTemplate">使用模板</span>
+      </div>
+    </header>
+
+    <div class="main-container">
+      <div v-loading="loading" class="template-list">
+        <el-table
+          ref="templateList"
+          :data="template_list"
+          height="580"
+          highlight-current-row
+          @current-change="selectCourseware"
+        >
+          <el-table-column label="序号" width="60">
+            <template #default="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="id" label="编号" />
+          <el-table-column prop="name" label="名称" />
+        </el-table>
+        <PaginationPage :total="total" @getList="getTemplateList" />
+      </div>
+
+      <div class="courseware-preview">
+        <CoursewarePreview
+          v-if="coursewareData.row_list.length > 0"
+          ref="courserware"
+          :is-show-group="false"
+          :group-show-all="true"
+          :group-row-list="content_group_row_list"
+          :data="coursewareData"
+          :courseware-id="curSelectId"
+          :component-list="component_list"
+          :background="background"
+          :can-remark="false"
+          :show-remark="false"
+        />
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { unified_attrib } from '@/common/data';
+import { getRandomNumber } from '@/utils/index';
+import { PageQueryTemplateList_EditBookSelect } from '@/api/list';
+import { ContentGetCoursewareContent_View, ContentGetCoursewareContent } from '@/api/book';
+
+import PaginationPage from '@/components/PaginationPage.vue';
+import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
+
+export default {
+  name: 'UseTemplate',
+  components: {
+    PaginationPage,
+    CoursewarePreview,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      loading: false,
+      template_list: [],
+      total: 0,
+      name: '',
+      coursewareData: { row_list: [] },
+      background: {
+        background_image_url: '',
+        background_position: {
+          left: 0,
+          top: 0,
+        },
+      },
+      component_list: [],
+      content_group_row_list: [],
+      curSelectId: '',
+      data: {
+        background_image_url: '',
+        background_position: {
+          width: 100,
+          height: 100,
+          top: 0,
+          left: 0,
+        },
+        // 组件列表
+        row_list: [],
+        // 全文设置
+        unified_attrib,
+      },
+      data_content_group_row_list: [],
+    };
+  },
+  methods: {
+    dialogClose() {
+      this.$emit('update:visible', false);
+    },
+    selectCourseware({ courseware_id }) {
+      this.curSelectId = courseware_id;
+      this.getCoursewareComponentContent_View(courseware_id);
+      this.getCoursewareContent(courseware_id);
+    },
+    /**
+     * 获取模板列表
+     */
+    getTemplateList(data) {
+      this.loading = true;
+      PageQueryTemplateList_EditBookSelect({
+        ...data,
+        storage_type: 0,
+        name: this.name,
+      })
+        .then(({ template_list, total_count }) => {
+          this.template_list = template_list;
+          this.total = total_count;
+        })
+        .finally(() => {
+          this.loading = false;
+        });
+    },
+    /**
+     * 得到课件内容(展示内容)
+     * @param {string} id - 课件ID
+     */
+    getCoursewareComponentContent_View(id) {
+      ContentGetCoursewareContent_View({ id }).then(({ content, component_list, content_group_row_list }) => {
+        if (content) {
+          const _content = JSON.parse(content);
+          this.coursewareData = _content;
+          this.background = {
+            background_image_url: _content.background_image_url,
+            background_position: _content.background_position,
+          };
+        } else {
+          this.coursewareData = { row_list: [] };
+        }
+
+        if (component_list) this.component_list = component_list;
+        if (content_group_row_list) this.content_group_row_list = JSON.parse(content_group_row_list) || [];
+      });
+    },
+    /**
+     * 得到课件内容(编辑内容)
+     * @param {string} id - 课件ID
+     */
+    getCoursewareContent(id) {
+      ContentGetCoursewareContent({ id }).then(({ content, content_group_row_list }) => {
+        if (content) {
+          let parsedContent = JSON.parse(content);
+          if (
+            parsedContent &&
+            Array.isArray(parsedContent.row_list) &&
+            parsedContent.row_list.length > 0 &&
+            !parsedContent.row_list[0]?.row_id
+          ) {
+            this.data = this.normalizeRowColIds(parsedContent);
+          } else {
+            this.data = parsedContent;
+          }
+
+          if (content_group_row_list) this.data_content_group_row_list = JSON.parse(content_group_row_list);
+        }
+      });
+    },
+    /**
+     * 归一化 row_list 中的 row_id / col_id(为后端旧数据补 id)
+     */
+    normalizeRowColIds(data) {
+      if (!data || !Array.isArray(data.row_list)) return data;
+      data.row_list = data.row_list.map((row) => {
+        if (!row.row_id) row.row_id = `R${getRandomNumber(6, true)}`;
+        if (!Array.isArray(row.col_list)) row.col_list = [];
+        row.col_list = row.col_list.map((col) => {
+          if (!col.col_id) col.col_id = `C${getRandomNumber(8, true)}`;
+          return col;
+        });
+        return row;
+      });
+      return data;
+    },
+    /**
+     * 使用模板
+     */
+    useTemplate() {
+      if (!this.curSelectId) {
+        this.$message.warning('请选择模板课件');
+        return;
+      }
+      this.$emit('useTemplate', {
+        data: this.data,
+        content_group_row_list: this.data_content_group_row_list,
+        temporaryCoursewareID: this.curSelectId,
+      });
+      this.dialogClose();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.use-template-dialog {
+  .dialog-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 10px;
+    border-bottom: 1px solid #e8e8e8;
+
+    .title {
+      width: 200px;
+      font-size: 18px;
+      font-weight: bold;
+      border-right: $border;
+    }
+  }
+
+  .main-container {
+    display: flex;
+    column-gap: 20px;
+    height: 600px;
+
+    .template-list {
+      width: 400px;
+    }
+
+    .courseware-preview {
+      flex: 1;
+      overflow: auto;
+    }
+  }
+}
+</style>

+ 28 - 1
src/views/personal_workbench/edit_task/edit/index.vue

@@ -28,6 +28,8 @@
               <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
             </el-select>
           </span>
+
+          <span class="link" @click="useTemplate">使用模板</span>
           <span class="link" @click="showSetBackground">背景图</span>
           <span class="link" @click="saveCoursewareContent('quit')">退出编辑</span>
           <span class="link" @click="saveCoursewareContent">保存</span>
@@ -38,14 +40,16 @@
     </div>
 
     <CreatePage ref="create" class="edit-task__content" @goBackPreview="goBackPreview" />
+    <UseTemplate :visible.sync="visibleTemplate" @useTemplate="handleUseTemplate" />
   </div>
 </template>
 
 <script>
 import CreatePage from '@/views/book/courseware/create/index.vue';
 import MenuPage from '@/views/personal_workbench/common/menu.vue';
-import * as OpenCC from 'opencc-js';
+import UseTemplate from './UseTemplate.vue';
 
+import * as OpenCC from 'opencc-js';
 import { GetBookCoursewareInfo, GetMyBookCoursewareTaskList } from '@/api/project';
 import { GetLanguageTypeList } from '@/api/book';
 
@@ -54,6 +58,7 @@ export default {
   components: {
     CreatePage,
     MenuPage,
+    UseTemplate,
   },
   provide() {
     return {
@@ -61,6 +66,7 @@ export default {
       getChinese: () => this.chinese,
       getLangList: () => this.langList,
       convertText: this.convertText,
+      getTemporaryCoursewareID: () => this.temporaryCoursewareID,
     };
   },
   data() {
@@ -74,6 +80,8 @@ export default {
       langList: [],
       lang: 'ZH',
       chinese: 'zh-Hans',
+      visibleTemplate: false,
+      temporaryCoursewareID: '',
     };
   },
   created() {
@@ -113,6 +121,7 @@ export default {
       }
     },
     saveCoursewareContent(type = '') {
+      this.temporaryCoursewareID = '';
       this.$refs.create.saveCoursewareContent(type);
     },
     showFullTextSettings() {
@@ -150,6 +159,24 @@ export default {
       }
       return text;
     },
+    /**
+     * 使用模板
+     */
+    useTemplate() {
+      this.visibleTemplate = true;
+    },
+    /**
+     * 处理使用模板事件
+     * @param {Object} param
+     * @param {Object} param.data - 模板数据
+     * @param {Array} param.content_group_row_list - 内容分组行列表
+     * @param {string} param.temporaryCoursewareID - 临时课件ID
+     */
+    handleUseTemplate({ data, content_group_row_list, temporaryCoursewareID }) {
+      this.$refs.create.loadTemplateData_Create({ data, content_group_row_list });
+      this.temporaryCoursewareID = temporaryCoursewareID;
+      this.visibleTemplate = false;
+    },
   },
 };
 </script>

+ 18 - 0
src/views/personal_workbench/edit_task/preview/index.vue

@@ -4,6 +4,7 @@
 
     <CommonPreview :id="id" ref="preview" :project-id="project_id">
       <template #operator="{ courseware }">
+        <span class="link" @click="saveCoursewareAsTemplate">保存为个人模板</span>
         <span v-if="isTrue(courseware.is_can_start_edit)" class="link" @click="editTask">开始编辑</span>
         <span v-if="isTrue(courseware.is_can_submit_audit)" class="link" @click="submitCoursewareToAuditFlow">
           提交审校
@@ -20,6 +21,7 @@ import CommonPreview from '@/components/CommonPreview.vue';
 
 import { SubmitBookCoursewareToAuditFlow } from '@/api/project';
 import { isTrue } from '@/utils/validate';
+import { SaveCoursewareAsTemplatePersonal } from '@/api/template';
 
 export default {
   name: 'TaskPreviewPage',
@@ -53,6 +55,22 @@ export default {
         this.$refs.preview.getBookCoursewareInfo(this.id);
       });
     },
+    /**
+     * 保存为个人模板
+     */
+    saveCoursewareAsTemplate() {
+      this.$confirm('确定要将当前课件保存为个人模板吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          SaveCoursewareAsTemplatePersonal({ courseware_id: this.$refs.preview.select_node }).then(() => {
+            this.$message.success('已保存为个人模板');
+          });
+        })
+        .catch(() => {});
+    },
   },
 };
 </script>

+ 0 - 2
src/views/personal_workbench/project/components/MenuTree.vue

@@ -140,5 +140,3 @@ export default {
   }
 }
 </style>
-
-<style lang="scss"></style>