2 Commits 32ab85317e ... 02f38c29f4

Auteur SHA1 Bericht Datum
  dsy 02f38c29f4 优化构建背景样式方法,支持动态更新 4 dagen geleden
  dsy 6d010df946 排序题支持空格分割完整句子 4 dagen geleden

+ 1 - 1
.env

@@ -11,4 +11,4 @@ VUE_APP_BookWebSI = '/GCLSBookWebSI/ServiceInterface'
 VUE_APP_EepServer = '/EEPServer/SI'
 
 #version
-VUE_APP_VERSION = '2026.05.22'
+VUE_APP_VERSION = '2026.05.25'

+ 18 - 17
src/components/CommonPreview.vue

@@ -111,11 +111,11 @@
           <SvgIcon icon-class="catalogue" size="54" />
         </div>
 
-        <main :class="['preview-main', { 'no-audit': !isShowAudit }]" :style="computedCommonPreviewStyle()">
+        <main :class="['preview-main', { 'no-audit': !isShowAudit }]" :style="computedCommonPreviewStyle">
           <div class="preview-left" :style="{ backgroundColor: background.background?.is_global ? '' : '#fff' }"></div>
           <CoursewarePreview
             v-if="courseware_info.book_name"
-            ref="courserware"
+            ref="courseware"
             :is-show-group="isShowGroup"
             :group-show-all="groupShowAll"
             :group-row-list="content_group_row_list"
@@ -516,6 +516,7 @@ import {
 import { toggleFullScreen } from '@/utils/common';
 import * as OpenCC from 'opencc-js';
 import { isTrue } from '@/utils/validate';
+import { buildCoursewareStyle } from '@/views/book/courseware/preview/common/utils/coursewareStyle';
 import { CreateCoursewarePreviewURL } from '@/api/app';
 
 export default {
@@ -742,6 +743,9 @@ export default {
       if (this.dialogType === 2) return '反馈';
       return '笔记';
     },
+    computedCommonPreviewStyle() {
+      return buildCoursewareStyle(this.background, 'commonPreview');
+    },
   },
   watch: {
     isJudgeCorrect(newVal) {
@@ -1020,7 +1024,7 @@ export default {
           this.submit_loading = false;
           this.visibleRemark = false;
           this.getCoursewareAuditRemarkList(id || this.id);
-          this.$refs.courserware.resetRemark();
+          this.$refs.courseware.resetRemark();
         })
         .catch(() => {
           this.submit_loading = false;
@@ -1029,7 +1033,7 @@ export default {
     // 关闭审核批注弹窗
     closeVisibleRemark() {
       this.visibleRemark = false;
-      this.$refs.courserware.resetRemark();
+      this.$refs.courseware.resetRemark();
     },
     // 删除批注
     deleteRemarks(id) {
@@ -1049,7 +1053,7 @@ export default {
     // 定位批注
     async handleLocationRemarks(offsetTop) {
       // if (componentId) {
-      //   let node = await this.$refs.courserware.findChildComponentByKey(componentId);
+      //   let node = await this.$refs.courseware.findChildComponentByKey(componentId);
       //   if (node) {
       await this.$nextTick();
       this.$refs.previewMain.scrollTo({
@@ -1062,7 +1066,7 @@ export default {
     },
     // 计算previewMain滑动距离
     computeScroll() {
-      this.$refs.courserware.handleResult(
+      this.$refs.courseware.handleResult(
         this.$refs.previewMain.scrollTop,
         this.$refs.previewMain.scrollLeft,
         this.select_node,
@@ -1105,7 +1109,7 @@ export default {
       let [nodeId, componentId] = data.split('#');
       if (nodeId) this.selectNode(nodeId);
       if (componentId) {
-        let node = await this.$refs.courserware.findChildComponentByKey(componentId);
+        let node = await this.$refs.courseware.findChildComponentByKey(componentId);
         if (node) {
           await this.$nextTick();
           this.$refs.previewMain.scrollTo({
@@ -1243,7 +1247,7 @@ export default {
     async handleFileClick(courseware_id, component_id) {
       if (courseware_id) this.selectNode(courseware_id);
       if (component_id) {
-        let node = await this.$refs.courserware.findChildComponentByKey(component_id);
+        let node = await this.$refs.courseware.findChildComponentByKey(component_id);
         if (node) {
           await this.$nextTick();
           this.$refs.previewMain.scrollTo({
@@ -1310,7 +1314,7 @@ export default {
     },
 
     simulateAnswer(disabled = true) {
-      this.$refs.courserware.simulateAnswer(this.isJudgeCorrect, this.isShowAnswer, disabled);
+      this.$refs.courseware.simulateAnswer(this.isJudgeCorrect, this.isShowAnswer, disabled);
     },
 
     /**
@@ -1325,7 +1329,7 @@ export default {
       this.selectNode(nodeId);
       this.isShowGroup = false;
       this.groupShowAll = true;
-      this.$refs.courserware.clearRowCheckList();
+      this.$refs.courseware.clearRowCheckList();
     },
     /**
      * 计算章节名称样式
@@ -1403,9 +1407,9 @@ export default {
         return;
       }
 
-      if (this.$refs.courserware && this.$refs.courserware.handleLocation) {
+      if (this.$refs.courseware && this.$refs.courseware.handleLocation) {
         item.type = type;
-        this.$refs.courserware.handleLocation(item);
+        this.$refs.courseware.handleLocation(item);
       }
     },
 
@@ -1754,10 +1758,10 @@ export default {
      * @returns {object} 选中分组行的课件信息
      */
     computedSelectedGroupCoursewareInfo() {
-      return this.$refs.courserware.computedSelectedGroupCoursewareInfo();
+      return this.$refs.courseware.computedSelectedGroupCoursewareInfo();
     },
     saveCoursewareStyleTemplate(data) {
-      return this.$refs.courserware.saveCoursewareStyleTemplate(data);
+      return this.$refs.courseware.saveCoursewareStyleTemplate(data);
     },
 
     async submitChapterAllCoursewareToAuditFlow(chapter_id) {
@@ -1792,9 +1796,6 @@ export default {
         this.$set(component, 'background', background);
       }
     },
-    computedCommonPreviewStyle() {
-      return this.$refs.courserware?.computedCourserwareStyle('commonPreview');
-    },
     /**
      * 获取 main-container 视口内第一个可见的组件 id
      * @returns {string} 第一个可见组件 id;不存在时返回空字符串

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

@@ -128,7 +128,7 @@ export default {
   },
   mounted() {
     const element = this.$refs.previewRoot;
-    // 监听 courserware 高度变化,获取其高度
+    // 监听 courseware 高度变化,获取其高度
     this.resizeObserver = new ResizeObserver(() => {
       const rect = element.getBoundingClientRect();
       this.heightPrompt = rect.height > 1620;

+ 2 - 2
src/views/book/courseware/create/components/SetBackground.vue

@@ -278,7 +278,7 @@ export default {
         const isGlobal = this.backgroundData.is_global;
         const courseware = isGlobal
           ? document.querySelector('main.preview-main')
-          : document.querySelector('div.courserware');
+          : document.querySelector('div.courseware');
         if (courseware) {
           const rect = courseware.getBoundingClientRect();
           const coursewareRatio = rect.width / rect.height;
@@ -316,7 +316,7 @@ export default {
     'background.is_global'(newVal) {
       const courseware = newVal
         ? document.querySelector('main.preview-main')
-        : document.querySelector('div.courserware');
+        : document.querySelector('div.courseware');
       if (courseware) {
         const rect = courseware.getBoundingClientRect();
         const coursewareRatio = rect.width / rect.height;

+ 8 - 3
src/views/book/courseware/create/components/question/fill/Fill.vue

@@ -459,8 +459,13 @@ export default {
 }
 
 .pinyin-text-list {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: center;
+  :deep .pinyin-area {
+    display: inline;
+  }
+
+  :deep .pinyin-area .rich-text-container,
+  :deep .pinyin-area .pinyin-paragraph {
+    display: inline;
+  }
 }
 </style>

+ 29 - 0
src/views/book/courseware/create/components/question/sort/Sort.vue

@@ -1,6 +1,15 @@
 <template>
   <ModuleBase ref="base" :type="data.type">
     <template #content>
+      <el-input
+        v-model="data.content"
+        type="textarea"
+        placeholder="你 喜欢 喝 茶 还是 咖啡 ?"
+        :autosize="{ minRows: 2, maxRows: 6 }"
+        resize="none"
+        @change="handleContentChange"
+      />
+
       <div class="sort-wrapper" :style="getSortWrapperStyle()">
         <template v-for="(item, i) in data.option_list">
           <div
@@ -181,6 +190,25 @@ export default {
       item.custom_serial_number = item.custom_serial_number.replace(/[^0-9/]/g, '');
     },
     /**
+     * @description 处理内容输入,自动根据空格分隔生成选项内容
+     */
+    handleContentChange() {
+      const words = (this.data.content || '').trim().split(/\s+/).filter(Boolean); // 根据空格分隔输入内容,并过滤掉空字符串
+      const targetCount = Math.max(this.data.option_list.length, words.length);
+
+      while (this.data.option_list.length < targetCount) {
+        this.data.option_list.push(getOption());
+      }
+
+      this.data.option_list.forEach((item, i) => {
+        item.content = words[i] || '';
+      });
+
+      if (this.data.property.option_count !== targetCount) {
+        this.data.property.option_count = targetCount;
+      }
+    },
+    /**
      * @description 处理思维导图数据
      */
     handleMindMap() {
@@ -210,6 +238,7 @@ export default {
 .sort-wrapper {
   display: grid;
   gap: 12px;
+  margin-top: 16px;
 
   .sort-rich {
     position: relative;

+ 1 - 0
src/views/book/courseware/data/sort.js

@@ -57,6 +57,7 @@ export function getSortData() {
     mind_map: {
       node_list: [{ name: '3选项横排设定显示排序组件' }],
     },
+    content: '',
     answer_list: [], // 答案列表
     analysis_list: [], // 解析列表
   };

+ 17 - 89
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -1,9 +1,9 @@
 <template>
   <div
     id="selectable-area-preview"
-    ref="courserware"
-    class="courserware"
-    :style="computedCourserwareStyle('courseware')"
+    ref="courseware"
+    class="courseware"
+    :style="computedCoursewareStyle"
     @mouseup="handleTextSelection"
     @mousedown="startSelection"
     @mousemove="updateSelection"
@@ -174,6 +174,7 @@
 <script>
 import { previewComponentList } from '@/views/book/courseware/data/bookType';
 import { getToken, getConfig } from '@/utils/auth';
+import { buildCoursewareStyle } from '@/views/book/courseware/preview/common/utils/coursewareStyle';
 import _ from 'lodash';
 const Base64 = require('js-base64').Base64;
 
@@ -267,6 +268,12 @@ export default {
       visible_id: this.$route.query?.visible_id || '', // 可见组件 id
     };
   },
+  computed: {
+    // 计算课件背景样式
+    computedCoursewareStyle() {
+      return buildCoursewareStyle(this.background, 'courseware');
+    },
+  },
   watch: {
     groupRowList: {
       handler(val) {
@@ -286,7 +293,7 @@ export default {
     },
   },
   mounted() {
-    const element = this.$refs.courserware;
+    const element = this.$refs.courseware;
     const rect = element.getBoundingClientRect();
     this.divPosition = {
       left: rect.left,
@@ -464,9 +471,10 @@ export default {
 
             // 如果有标签类组件,获取对应名称
             if (findKey) {
-              let item = this.isEdit
-                ? this.findChildComponentByKey(`grid-${findKey}`)
-                : this.$refs.previewEdit.findChildComponentByKey(`preview-${findKey}`);
+              let item = this.$refs.preview.find(
+                (child) => child.$el && child.$el.dataset && child.$el.dataset.id === findKey,
+              );
+
               if (['describe', 'stem'].includes(findType)) {
                 groupName = item.data.content.replace(/<[^>]+>/g, '');
               } else if (findType === 'label') {
@@ -655,86 +663,6 @@ export default {
         gridTemplateRows,
       };
     },
-
-    /**
-     * 计算课件背景样式
-     * @param {'courseware' | 'commonPreview'} type 从哪个组件调用 'courseware' 或 'commonPreview'
-     * @returns {object} 课件背景样式对象
-     */
-    computedCourserwareStyle(type) {
-      const {
-        background_image_url: bcImgUrl = '',
-        background_position: pos = {},
-        background: back,
-      } = this.background || {};
-
-      // 如果是 commonPreview 但背景不是全域的,或者是 courseware 但背景是全域的,都不应用背景样式
-      if (type === 'commonPreview' && !back?.is_global) {
-        return {};
-      }
-      if (type === 'courseware' && back?.is_global) {
-        return {};
-      }
-
-      let canvasStyle = {
-        backgroundSize: bcImgUrl ? `${pos.width}% ${pos.height}%` : '',
-        backgroundPosition: bcImgUrl ? `${pos.left}% ${pos.top}%` : '',
-        backgroundImage: bcImgUrl ? `url(${bcImgUrl})` : '',
-      };
-
-      if (back) {
-        if (!back.has_image) {
-          canvasStyle['backgroundBlendMode'] = '';
-          canvasStyle['backgroundImage'] = '';
-          canvasStyle['backgroundRepeat'] = '';
-          canvasStyle['backgroundPosition'] = '';
-          canvasStyle['backgroundSize'] = '';
-        }
-
-        if (back.imageMode === 'fill') {
-          canvasStyle['backgroundRepeat'] = 'repeat';
-          canvasStyle['backgroundSize'] = '';
-          canvasStyle['backgroundPosition'] = '';
-        } else {
-          canvasStyle['backgroundRepeat'] = 'no-repeat';
-        }
-
-        if (back.imageMode === 'stretch') {
-          canvasStyle['backgroundSize'] = '100% 100%';
-        }
-
-        if (back.imageMode === 'adapt') {
-          canvasStyle['backgroundSize'] = 'contain';
-        }
-
-        if (back.imageMode === 'auto') {
-          canvasStyle['backgroundPosition'] = `${pos.imgX}% ${pos.imgY}%`;
-        }
-
-        if (back.has_color) {
-          canvasStyle['backgroundColor'] = back.color;
-        } else {
-          canvasStyle['backgroundColor'] = '#fff';
-        }
-
-        if (back.enable_border) {
-          canvasStyle['border'] = `${back.border_width}px ${back.border_style} ${back.border_color}`;
-        } else {
-          canvasStyle['border'] = 'none';
-        }
-
-        if (back.enable_radius) {
-          canvasStyle['border-top-left-radius'] = `${back.top_left_radius}px`;
-          canvasStyle['border-top-right-radius'] = `${back.top_right_radius}px`;
-          canvasStyle['border-bottom-left-radius'] = `${back.bottom_left_radius}px`;
-          canvasStyle['border-bottom-right-radius'] = `${back.bottom_right_radius}px`;
-        } else {
-          canvasStyle['border-radius'] = '0';
-        }
-      }
-
-      return canvasStyle;
-    },
     handleContextMenu(event, id) {
       if (this.canRemark) {
         event.preventDefault(); // 阻止默认的上下文菜单显示
@@ -861,7 +789,7 @@ export default {
         this.selectHandleInfo = selectHandleInfo;
 
         if (!this.canRemark) this.showToolbar = true;
-        const container = document.querySelector('.courserware');
+        const container = document.querySelector('.courseware');
         const boxRect = container.getBoundingClientRect();
         const selectRect = range.getBoundingClientRect();
         this.contentmenu = {
@@ -1227,7 +1155,7 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.courserware {
+.courseware {
   position: relative;
   display: flex;
   flex-direction: column;

+ 77 - 0
src/views/book/courseware/preview/common/utils/coursewareStyle.js

@@ -0,0 +1,77 @@
+/**
+ * 构建课件背景样式
+ * @param {object} background 背景信息
+ * @param {string} type 预览类型,commonPreview: 公共预览,courseware: 课件预览
+ * @returns {object} 样式对象
+ */
+export function buildCoursewareStyle(background, type) {
+  const { background_image_url: bcImgUrl = '', background_position: pos = {}, background: back } = background || {};
+
+  if (type === 'commonPreview' && !back?.is_global) {
+    return {};
+  }
+  if (type === 'courseware' && back?.is_global) {
+    return {};
+  }
+
+  const canvasStyle = {
+    backgroundSize: bcImgUrl ? `${pos.width}% ${pos.height}%` : '',
+    backgroundPosition: bcImgUrl ? `${pos.left}% ${pos.top}%` : '',
+    backgroundImage: bcImgUrl ? `url(${bcImgUrl})` : '',
+  };
+
+  if (!back) {
+    return canvasStyle;
+  }
+
+  if (!back.has_image) {
+    canvasStyle.backgroundBlendMode = '';
+    canvasStyle.backgroundImage = '';
+    canvasStyle.backgroundRepeat = '';
+    canvasStyle.backgroundPosition = '';
+    canvasStyle.backgroundSize = '';
+  }
+
+  if (back.imageMode === 'fill') {
+    canvasStyle.backgroundRepeat = 'repeat';
+    canvasStyle.backgroundSize = '';
+    canvasStyle.backgroundPosition = '';
+  } else {
+    canvasStyle.backgroundRepeat = 'no-repeat';
+  }
+
+  if (back.imageMode === 'stretch') {
+    canvasStyle.backgroundSize = '100% 100%';
+  }
+
+  if (back.imageMode === 'adapt') {
+    canvasStyle.backgroundSize = 'contain';
+  }
+
+  if (back.imageMode === 'auto') {
+    canvasStyle.backgroundPosition = `${pos.imgX}% ${pos.imgY}%`;
+  }
+
+  if (back.has_color) {
+    canvasStyle.backgroundColor = back.color;
+  } else {
+    canvasStyle.backgroundColor = '#fff';
+  }
+
+  if (back.enable_border) {
+    canvasStyle.border = `${back.border_width}px ${back.border_style} ${back.border_color}`;
+  } else {
+    canvasStyle.border = 'none';
+  }
+
+  if (back.enable_radius) {
+    canvasStyle['border-top-left-radius'] = `${back.top_left_radius}px`;
+    canvasStyle['border-top-right-radius'] = `${back.top_right_radius}px`;
+    canvasStyle['border-bottom-left-radius'] = `${back.bottom_left_radius}px`;
+    canvasStyle['border-bottom-right-radius'] = `${back.bottom_right_radius}px`;
+  } else {
+    canvasStyle.borderRadius = '0';
+  }
+
+  return canvasStyle;
+}

+ 14 - 10
src/views/book/courseware/preview/components/article/NotesModelChs.vue

@@ -1,6 +1,6 @@
 <!--  -->
 <template>
-  <div v-if="curQue" class="NNPE-ArticleView" id="notes-model">
+  <div v-if="curQue" id="notes-model" class="NNPE-ArticleView">
     <div
       v-if="
         ((curQue.mp3_list && curQue.mp3_list.length > 0 && curQue.mp3_list[0].url) ||
@@ -78,12 +78,12 @@
                   }"
                 />
                 <video
+                  v-else
                   :src="item.sourceList[0].file_url_open"
                   width="100%"
                   height="400"
                   controls
                   controlsList="nodownload"
-                  v-else
                 ></video>
               </template>
 
@@ -565,12 +565,12 @@
                   }"
                 />
                 <video
+                  v-else
                   :src="item.sourceList[0].file_url_open"
                   width="100%"
                   height="400"
                   controls
                   controlsList="nodownload"
-                  v-else
                 ></video>
               </template>
             </div>
@@ -1051,6 +1051,7 @@
       </div>
     </div>
     <el-dialog
+      v-if="notesFlag"
       :visible.sync="notesFlag"
       :show-close="true"
       :title="'笔记'"
@@ -1058,14 +1059,19 @@
       width="367px"
       class="notes-dialog"
       @close="handleData"
-      v-if="notesFlag"
     >
       <p>{{ notesObj.title }}</p>
-      <el-input type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="请输入" v-model="notesObj.notes">
-      </el-input>
+      <el-input
+        v-model="notesObj.notes"
+        type="textarea"
+        :rows="4"
+        maxlength="200"
+        show-word-limit
+        placeholder="请输入"
+      />
       <div class="btn-box">
-        <el-button type="danger" plain size="small" @click="deleteNotes" v-if="notesObj.id">删除</el-button>
-        <el-button type="primary" @click="handleSave" size="small" :loading="loading">保存</el-button>
+        <el-button v-if="notesObj.id" type="danger" plain size="small" @click="deleteNotes">删除</el-button>
+        <el-button type="primary" size="small" :loading="loading" @click="handleSave">保存</el-button>
       </div>
     </el-dialog>
   </div>
@@ -1369,7 +1375,6 @@ export default {
           .on('selection:create', ({ sources }) => {
             // sources = sources.map(hs => ({hs}));
             if (sources && sources[0]) {
-              console.log(sources[0]);
               _this.notesObj.title = sources[0].text;
               _this.notesObj.pos = JSON.stringify({
                 sent_id: _this.activeSentObj,
@@ -1532,7 +1537,6 @@ export default {
       // });
     },
     mouseupClick(obj, index) {
-      console.log(obj);
       this.activeSentObj = index;
     },
   }, // 如果页面有keep-alive缓存功能,这个函数会触发

+ 1 - 0
src/views/book/courseware/preview/components/sort/SortPreview.vue

@@ -210,6 +210,7 @@ export default {
     getNoTextContentData() {
       let noTextContentData = JSON.parse(JSON.stringify(this.data));
       const resetFieldMap = {
+        content: '',
         analysis_list: [],
         answer_list: [],
       };

+ 1 - 1
src/views/personal_workbench/edit_task/edit/UseTemplate.vue

@@ -65,7 +65,7 @@
         </div>
         <CoursewarePreview
           v-if="coursewareData.row_list?.length > 0"
-          ref="courserware"
+          ref="courseware"
           :is-show-group="false"
           :group-show-all="true"
           :group-row-list="content_group_row_list"

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

@@ -149,7 +149,6 @@ export default {
           let courseware_info = {};
           if (form.is_select_part_courseware_mode === 'true') {
             courseware_info = this.$refs.preview.computedSelectedGroupCoursewareInfo();
-
             if (courseware_info?.component_id_list.length === 0) {
               this.$message.warning('请选择要保存的内容');
               return;

+ 1 - 1
src/views/personal_workbench/template_list/preview/CommonPreview.vue

@@ -39,7 +39,7 @@
           <div class="preview-left"></div>
           <CoursewarePreview
             v-if="courseware_info.book_name || courseware_info.name"
-            ref="courserware"
+            ref="courseware"
             :is-show-group="false"
             :group-show-all="true"
             :group-row-list="content_group_row_list"

+ 0 - 2
src/views/project_manage/org/project/index.vue

@@ -75,8 +75,6 @@ export default {
      * @param {string} request_status 申请状态
      */
     projectAuditOperate(project_id, is_pass, request_status) {
-      console.log(request_status);
-
       this.$confirm(`确定要执行${is_pass === 'true' ? '审核通过' : '审核拒绝'}吗?`, '提示', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',