Przeglądaj źródła

1、组件正确答案显示 2、el-dialog组件拖拽指令

dsy 3 dni temu
rodzic
commit
7675fa31db

+ 0 - 1
src/components/CommonPreview.vue

@@ -12,7 +12,6 @@
           <el-checkbox v-model="isShowGroup">显示分组</el-checkbox>
           <el-checkbox v-model="groupShowAll">分组显示全部</el-checkbox>
           <el-checkbox v-model="isJudgeCorrect">判断对错</el-checkbox>
-          <el-checkbox v-model="isShowAnswer" :disabled="!isJudgeCorrect">显示答案</el-checkbox>
         </div>
         <span class="link">
           <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select">

+ 66 - 0
src/utils/directive.js

@@ -11,3 +11,69 @@ Vue.directive('numericOnly', {
     });
   },
 });
+
+/**
+ * el-dialog 组件拖拽指令
+ */
+Vue.directive('dialogDrag', {
+  bind(el) {
+    const dialog = el.querySelector('.el-dialog');
+    const header = el.querySelector('.el-dialog__header');
+    if (!dialog || !header) return;
+
+    header.style.cursor = 'move';
+
+    const resetPosition = () => {
+      dialog.style.left = '';
+      dialog.style.top = '';
+      dialog.style.position = '';
+      dialog.style.margin = '';
+    };
+
+    // 当对话框隐藏时重置其位置
+    const observer = new MutationObserver(() => {
+      const isHidden = window.getComputedStyle(el).display === 'none';
+      if (isHidden) resetPosition();
+    });
+
+    // 监听 el 的 style 和 class 属性变化,以检测对话框的显示状态
+    observer.observe(el, {
+      attributes: true,
+      attributeFilter: ['style'],
+    });
+
+    el.__dialogDragCleanup__ = () => observer.disconnect();
+
+    header.onmousedown = (e) => {
+      const startX = e.clientX;
+      const startY = e.clientY;
+      const rect = dialog.getBoundingClientRect();
+      const startLeft = rect.left;
+      const startTop = rect.top;
+
+      const onMouseMove = (moveEvent) => {
+        const dx = moveEvent.clientX - startX;
+        const dy = moveEvent.clientY - startY;
+        dialog.style.margin = 0;
+        dialog.style.left = `${startLeft + dx}px`;
+        dialog.style.top = `${startTop + dy}px`;
+        dialog.style.position = 'fixed';
+      };
+
+      const onMouseUp = () => {
+        document.removeEventListener('mousemove', onMouseMove);
+        document.removeEventListener('mouseup', onMouseUp);
+      };
+
+      document.addEventListener('mousemove', onMouseMove);
+      document.addEventListener('mouseup', onMouseUp);
+    };
+  },
+  unbind(el) {
+    // 清理事件监听器和观察者
+    if (el.__dialogDragCleanup__) {
+      el.__dialogDragCleanup__();
+      delete el.__dialogDragCleanup__;
+    }
+  },
+});

+ 29 - 0
src/views/book/courseware/preview/common/AnswerAnalysis.vue

@@ -1,6 +1,7 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
   <el-dialog
+    v-dialogDrag
     custom-class="answer-analysis-dialog"
     :visible="visible"
     width="65vw"
@@ -9,6 +10,10 @@
   >
     <!-- 正确答案 -->
     <div class="right-answer">
+      <div class="right-answer-title">
+        <span class="right-answer-title-icon"></span>
+        <span class="right-answer-title-image"></span>
+      </div>
       <slot name="right-answer"></slot>
     </div>
 
@@ -119,12 +124,36 @@ export default {
   methods: {
     handleClose() {
       this.$emit('update:visible', false);
+      this.$emit('closeAnswerAnalysis');
     },
   },
 };
 </script>
 
 <style lang="scss" scoped>
+.right-answer {
+  margin-bottom: 16px;
+
+  .right-answer-title {
+    display: flex;
+    column-gap: 8px;
+    align-items: center;
+    margin-bottom: 8px;
+
+    &-icon {
+      width: 32px;
+      height: 32px;
+      background: url('@/assets/component/correct-answer-icon.png') no-repeat center/contain;
+    }
+
+    &-image {
+      width: 181px;
+      height: 32px;
+      background: url('@/assets/component/correct-answer.png') no-repeat center/contain;
+    }
+  }
+}
+
 .answer-list {
   width: 100%;
 

+ 9 - 1
src/views/book/courseware/preview/components/common/PreviewMixin.js

@@ -19,7 +19,7 @@ const mixin = {
       isShowParse: false, // 是否显示解析
       isEnable,
       loader: false,
-      visibleAnswerAnalysis: false,
+      visibleAnswerAnalysis: false, // 是否显示答案解析弹窗
     };
   },
   inject: ['getLang', 'getChinese', 'convertText', 'getTitleList'],
@@ -193,6 +193,14 @@ const mixin = {
     },
     showAnswerAnalysis() {
       this.visibleAnswerAnalysis = true;
+      this.disabled = true;
+      this.isJudgingRightWrong = true;
+      this.isShowRightAnswer = true;
+    },
+    closeAnswerAnalysis() {
+      this.disabled = false;
+      this.isJudgingRightWrong = false;
+      this.isShowRightAnswer = false;
     },
   },
 };

+ 86 - 7
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -29,7 +29,7 @@
                   :key="j"
                   v-model="li.content"
                   :disabled="disabled"
-                  :class="[data.property.fill_font, ...computedAnswerClass(li.mark)]"
+                  :class="[data.property.fill_font]"
                   :style="[{ width: Math.max(80, li.content.length * 21.3) + 'px' }]"
                 />
               </template>
@@ -51,7 +51,7 @@
                     slot="reference"
                     v-model="li.content"
                     :readonly="true"
-                    :class="[data.property.fill_font, ...computedAnswerClass(li.mark)]"
+                    :class="[data.property.fill_font]"
                     class="pinyin"
                     :style="[{ width: Math.max(80, li.content.length * 21.3) + 'px' }]"
                   />
@@ -82,10 +82,6 @@
                   @handleWav="handleMiniWav($event, li.mark)"
                 />
               </template>
-
-              <span v-show="computedAnswerText(li.mark).length > 0" :key="`answer-${j}`" class="right-answer">
-                {{ computedAnswerText(li.mark) }}
-              </span>
             </template>
           </template>
         </p>
@@ -112,7 +108,90 @@
       :visible.sync="visibleAnswerAnalysis"
       :answer-list="data.answer_list"
       :analysis-list="data.analysis_list"
-    />
+      @closeAnswerAnalysis="closeAnswerAnalysis"
+    >
+      <div slot="right-answer" class="fill-wrapper">
+        <p v-for="(item, i) in modelEssay" :key="i">
+          <template v-for="(li, j) in item">
+            <template v-if="li.type === 'text'">
+              <PinyinText
+                v-if="isEnable(data.property.view_pinyin)"
+                :key="`${i}-${j}`"
+                class="content"
+                :paragraph-list="li.paragraph_list"
+                :pinyin-position="data.property.pinyin_position"
+                :is-preview="true"
+              />
+              <span v-else :key="j" v-html="convertText(sanitizeHTML(li.content))"></span>
+            </template>
+            <template v-if="li.type === 'input'">
+              <template v-if="data.property.fill_type === fillTypeList[0].value">
+                <el-input
+                  :key="j"
+                  v-model="li.content"
+                  :disabled="disabled"
+                  :class="[data.property.fill_font, ...computedAnswerClass(li.mark)]"
+                  :style="[{ width: Math.max(80, li.content.length * 21.3) + 'px' }]"
+                />
+              </template>
+
+              <template v-else-if="data.property.fill_type === fillTypeList[1].value">
+                <el-popover :key="j" placement="top" trigger="click">
+                  <div class="word-list">
+                    <span
+                      v-for="{ content, mark } in data.word_list"
+                      :key="mark"
+                      class="word-item"
+                      @click="handleSelectWord(content, mark, li)"
+                    >
+                      {{ content }}
+                    </span>
+                  </div>
+
+                  <el-input
+                    slot="reference"
+                    v-model="li.content"
+                    :readonly="true"
+                    :class="[data.property.fill_font, ...computedAnswerClass(li.mark)]"
+                    class="pinyin"
+                    :style="[{ width: Math.max(80, li.content.length * 21.3) + 'px' }]"
+                  />
+                </el-popover>
+              </template>
+
+              <template v-else-if="data.property.fill_type === fillTypeList[2].value">
+                <span :key="j" class="write-click" @click="handleWriteClick(li.mark)">
+                  <img
+                    v-show="li.write_base64"
+                    style="background-color: #f4f4f4"
+                    :src="li.write_base64"
+                    alt="write-show"
+                  />
+                </span>
+              </template>
+
+              <template v-else-if="data.property.fill_type === fillTypeList[3].value">
+                <SoundRecordBox
+                  ref="record"
+                  :key="j"
+                  type="mini"
+                  :many-times="false"
+                  class="record-box"
+                  :attrib="data.unified_attrib"
+                  :answer-record-list="data.audio_answer_list"
+                  :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
+                  @handleWav="handleMiniWav($event, li.mark)"
+                />
+              </template>
+
+              <span v-show="computedAnswerText(li.mark).length > 0" :key="`answer-${j}`" class="right-answer">
+                {{ computedAnswerText(li.mark) }}
+              </span>
+            </template>
+          </template>
+        </p>
+      </div>
+    </AnswerAnalysis>
   </div>
 </template>