2
0

3 コミット af6731873f ... 8894523be6

作者 SHA1 メッセージ 日付
  dsy 8894523be6 组件分组问题修复 1 週間 前
  dsy dbf4df61d0 富文本组件,悬浮提示改为中文:行距。 1 週間 前
  dsy bae2ca13ea 1. bookEep 增加分组选择功能 2.填空题问题修正 1 週間 前

+ 1 - 1
.env

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

+ 26 - 2
packages/BookEep.vue

@@ -10,9 +10,10 @@
           ></div>
           <CoursewarePreview
             ref="courseware"
-            :is-show-group="false"
-            :group-show-all="true"
+            :is-show-group="isShowGroup"
+            :group-show-all="groupShowAll"
             :group-row-list="groupRowList"
+            :row-checked-list="rowCheckedList"
             :data="data"
             :courseware-id="curSelectId"
             :component-list="cList"
@@ -137,6 +138,14 @@ export default {
       type: String,
       default: '',
     },
+    isShowGroup: {
+      type: Boolean,
+      default: false,
+    },
+    rowChecked: {
+      type: String,
+      default: '{}',
+    },
   },
   data() {
     const isMobile = /android|iphone|ipad|ipod/i.test(navigator.userAgent || navigator.vendor || window.opera);
@@ -180,6 +189,7 @@ export default {
       allFeedbackList: [],
       drawerType: 1,
       isMobile,
+      groupShowAll: false, // 是否显示全部分组内容
     };
   },
   computed: {
@@ -190,6 +200,10 @@ export default {
       if (this.dialogType === 2) return '反馈';
       return '笔记';
     },
+    rowCheckedList() {
+      const _rowChecked = this.rowChecked === '[]' ? '{}' : this.rowChecked;
+      return _rowChecked ? JSON.parse(_rowChecked) : {};
+    },
   },
   watch: {
     componentList: {
@@ -247,6 +261,13 @@ export default {
       deep: true,
       immediate: true,
     },
+    rowCheckedList: {
+      handler(val) {
+        this.groupShowAll = Object.keys(val).length === 0;
+      },
+      deep: true,
+      immediate: true,
+    },
   },
   created() {
     const origin = window.location.origin;
@@ -660,6 +681,9 @@ export default {
         }
       });
     },
+    submitPreviewGroupId() {
+      return JSON.stringify(this.$refs.courseware.rowCheckList);
+    },
   },
 };
 </script>

+ 1 - 1
packages/package.json

@@ -1,6 +1,6 @@
 {
   "name": "eep-ui",
-  "version": "0.0.38",
+  "version": "0.0.41",
   "main": "eep/eep-ui.umd.js",
   "style": "eep/eep-ui.css",
   "exports": {

ファイルの差分が大きいため隠しています
+ 21 - 0
patches/tinymce+5.10.9.patch


+ 84 - 9
src/views/book/courseware/create/components/CreateCanvas.vue

@@ -1,11 +1,17 @@
 <template>
   <main ref="canvas" class="canvas">
     <div v-if="isEdit" class="edit">
-      <div v-for="item in lineList" :key="item[0]" class="group-line" :style="computedGroupLine(item)"></div>
+      <div
+        v-for="item in lineList"
+        :key="item[0]"
+        class="group-line"
+        :data-canvas-height="canvasHeight"
+        :style="computedGroupLine(item)"
+      ></div>
       <span class="drag-line" data-row="-1"></span>
       <!-- 行 -->
       <template v-for="(row, i) in data.row_list">
-        <div :key="row.row_id" class="row" :style="computedRowStyle(i)">
+        <div :key="`check-${row.row_id}`" class="row" :style="computedRowStyle(i)">
           <el-checkbox
             v-if="row?.row_id"
             v-model="rowCheckList[row.row_id]"
@@ -192,6 +198,7 @@ export default {
       projectResourcePopedom: [], // 当前编辑人员的项目资源权限
       componentsDataLoaded: false, // 组件数据是否加载完成
       visible_id: this.$route.query.visible_id, // 可见组件 id
+      canvasHeight: 0, // 画布高度,监听变化用于重绘分组线
     };
   },
   computed: {
@@ -268,13 +275,8 @@ export default {
           }
         });
 
-        // 过滤掉已经不存在的行,并将第一行的 is_pre_same_group 设置为 false
-        this.content_group_row_list = this.content_group_row_list.filter((group) =>
-          val.find((row) => row.row_id === group.row_id),
-        );
-        if (this.content_group_row_list[0]) {
-          this.content_group_row_list[0].is_pre_same_group = false;
-        }
+        // 过滤掉已经不存在的行,并修正行删除后的分组状态
+        this.content_group_row_list = this.normalizeGroupRowList(this.content_group_row_list, val);
       },
       immediate: true,
     },
@@ -411,11 +413,14 @@ export default {
     document.addEventListener('mousemove', this.dragMove);
     document.addEventListener('mouseup', this.dragEnd);
     window.addEventListener('keydown', this.handleCopy);
+    // 监听画布高度变化,重绘分组线
+    this.initCanvasResizeObserver();
   },
   beforeDestroy() {
     document.removeEventListener('mousemove', this.dragMove);
     document.removeEventListener('mouseup', this.dragEnd);
     window.removeEventListener('keydown', this.handleCopy);
+    this.destroyCanvasResizeObserver();
   },
   methods: {
     /**
@@ -1574,11 +1579,41 @@ export default {
     },
 
     /**
+     * 初始化画布 ResizeObserver,监听画布高度变化
+     * 高度变化时更新 canvasHeight,触发重新渲染以重绘分组线
+     */
+    initCanvasResizeObserver() {
+      const canvas = this.$refs.canvas;
+      if (!canvas || typeof ResizeObserver === 'undefined') return;
+
+      this.canvasResizeObserver = new ResizeObserver((entries) => {
+        entries.forEach((entry) => {
+          const height = entry.contentRect.height;
+          if (height !== this.canvasHeight) {
+            this.canvasHeight = height;
+          }
+        });
+      });
+      this.canvasResizeObserver.observe(canvas);
+    },
+    /**
+     * 销毁画布 ResizeObserver
+     */
+    destroyCanvasResizeObserver() {
+      if (this.canvasResizeObserver) {
+        this.canvasResizeObserver.disconnect();
+        this.canvasResizeObserver = null;
+      }
+    },
+
+    /**
      * 计算分组线样式
      * @param {Array} rowIdList 行 ID 列表
      * @returns {Object} 样式对象
      */
     computedGroupLine(rowIdList) {
+      // 画布高度变化时(canvasHeight 更新)会触发组件重渲染,此处重新读取 DOM 计算分组线位置
+
       // 获取画布顶部位置
       const canvas = this.$refs.canvas;
       const firstCheckbox = this.$el.querySelector(`.row-checkbox.${rowIdList[0]}`);
@@ -1682,6 +1717,46 @@ export default {
       return data;
     },
     /**
+     * 删除行后归一化行分组列表
+     *
+     * is_pre_same_group 为 true 表示该行与其前一行同属一个分组。
+     * 行删除后,content_group_row_list 中剩余行的 is_pre_same_group 仍是删除前的值,
+     * 若某分组的首行被删除,则组内剩余首行的 is_pre_same_group 需置为 false,
+     * 否则分组线(.group-line)会错误地连接到前一组。
+     *
+     * @param {Array} groupList 删除前(含新增行)的行分组列表
+     * @param {Array} rowList 删除后的 row_list
+     * @returns {Array} 归一化后的行分组列表
+     */
+    normalizeGroupRowList(groupList, rowList) {
+      const validRowIds = new Set((rowList || []).map((row) => row.row_id));
+
+      // 按删除前的分组划分:is_pre_same_group 为 false 的行是新分组的首行
+      const groups = [];
+      let curGroup = [];
+      (groupList || []).forEach((item) => {
+        if (item.is_pre_same_group) {
+          curGroup.push(item);
+        } else {
+          if (curGroup.length) groups.push(curGroup);
+          curGroup = [item];
+        }
+      });
+      if (curGroup.length) groups.push(curGroup);
+
+      // 过滤掉已删除的行,并将每组剩余首行的 is_pre_same_group 置为 false,其余保持 true
+      const result = [];
+      groups.forEach((group) => {
+        const remaining = group.filter((item) => validRowIds.has(item.row_id));
+        if (!remaining.length) return;
+        remaining.forEach((item, i) => {
+          item.is_pre_same_group = i > 0;
+        });
+        result.push(...remaining);
+      });
+      return result;
+    },
+    /**
      * 加载模板数据
      * @param {Object} param
      * @param {Object} param.data - 模板数据

+ 22 - 9
src/views/book/courseware/create/components/question/fill/Fill.vue

@@ -101,7 +101,7 @@
             :key="item.mark"
             v-model="item.value"
             placeholder="多个答案可用‘/’分割"
-            @blur="handleTone(item.value, i)"
+            @blur="handleTone(item.value, item)"
           >
             <span slot="prefix">{{ i + 1 }}.</span>
           </el-input>
@@ -313,6 +313,19 @@ export default {
      */
     appendTextBlock(blocks, content, richTextList = []) {
       if (!this.hasVisibleText(content) && blocks.length > 0) {
+        // 填空(input)属于当前段落的一部分:若段落以填空结尾,紧随其后的闭合标签和
+        // 段间换行 \n 不能合并到填空前面的文本块中,否则 PinyinText 会把 \n 渲染成
+        // <br> 出现在填空之前,导致填空换到下一行显示。此时将换行保留为填空后的独立文本块。
+        const lastBlock = blocks[blocks.length - 1];
+        if (lastBlock && lastBlock.type === 'input' && String(content).includes('\n')) {
+          blocks.push({
+            content,
+            type: 'text',
+            rich_text_list: richTextList,
+          });
+          return;
+        }
+
         for (let i = blocks.length - 1; i >= 0; i--) {
           if (blocks[i] && blocks[i].type === 'text') {
             blocks[i].content = `${blocks[i].content || ''}${content}`;
@@ -720,18 +733,18 @@ export default {
       return slicedWord;
     },
 
-    handleTone(value, i) {
-      if (!/^[a-zA-Z0-9\s]+$/.test(value)) return;
-      this.data.answer.answer_list[i].value = value
+    handleTone(value, item) {
+      if (!item || !/^[a-zA-Z0-9\s]+$/.test(value)) return;
+      item.value = value
         .trim()
         .split(/\s+/)
-        .map((item) => {
-          return handleToneValue(item);
+        .map((valItem) => {
+          return handleToneValue(valItem);
         })
-        .map((item) =>
-          item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
+        .map((valItem) =>
+          valItem.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
         )
-        .filter((item) => item.length > 0)
+        .filter((valItem) => valItem.length > 0)
         .join(' ');
     },
     uploads(file_id) {

+ 11 - 0
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -242,6 +242,10 @@ export default {
       type: Boolean,
       default: false,
     },
+    rowCheckedList: {
+      type: Object,
+      default: () => ({}),
+    },
   },
   data() {
     return {
@@ -298,6 +302,13 @@ export default {
         this.courseware_id = val;
       },
     },
+    rowCheckedList: {
+      handler(val) {
+        this.rowCheckList = val;
+      },
+      immediate: true,
+      deep: true,
+    },
   },
   mounted() {
     const element = this.$refs.courseware;

+ 1 - 0
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -509,6 +509,7 @@ export default {
         if (li.type === 'input') {
           li.input = '';
           li.write_base64 = '';
+          li.audio_answer_list = [];
         }
       });
       this.selectedWordList = [];

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません