Browse Source

拼音效果加注释功能

zq 4 days ago
parent
commit
879e1b2b54

+ 1 - 1
src/components/ExplanatoryNoteDialog.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-dialog title="编辑注" :visible.sync="visible" width="680px" @close="dialogClose()">
+  <el-dialog title="编辑注" :visible.sync="visible" width="680px" @close="dialogClose()">
     <RichText
       v-model="richData.note"
       toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"

+ 77 - 1
src/components/PinyinText.vue

@@ -26,11 +26,25 @@
     </div>
 
     <CorrectPinyin :visible.sync="visible" :select-content="selectContent" @fillTonePinyin="fillTonePinyin" />
+
+    <el-dialog
+      ref="optimizedDialog"
+      title=""
+      :visible.sync="noteDialogVisible"
+      width="680px"
+      :style="dialogStyle"
+      :close-on-click-modal="false"
+      destroy-on-close
+      @close="noteDialogVisible = false"
+    >
+      <span v-html="sanitizeHTML(note)"></span>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import CorrectPinyin from '@/views/book/courseware/create/components/base/common/CorrectPinyin.vue';
+import { sanitizeHTML } from '@/utils/common';
 
 export default {
   name: 'PinyinText',
@@ -53,18 +67,80 @@ export default {
   },
   data() {
     return {
+      sanitizeHTML,
       paragraph_list: [],
       visible: false,
       selectContent: {},
       paragraph_index: 0,
       sentence_index: 0,
       word_index: 0,
+      noteDialogVisible: false,
+      note: '',
+      dialogStyle: {
+        position: 'fixed',
+        top: '0',
+        left: '0',
+        margin: '0',
+      },
     };
   },
   methods: {
     // 校对拼音
     correctPinyin(item, i, j, k) {
-      if (this.isPreview) return; // 如果是预览模式,不操作
+      if (this.isPreview) {
+        if (item.note) {
+          this.note = item.note;
+
+          this.noteDialogVisible = true;
+          this.$nextTick(() => {
+            const dialogElement = this.$refs.optimizedDialog;
+            // 确保对话框DOM已渲染
+            if (!dialogElement) {
+              return;
+            }
+            // 获取对话框内容区域的DOM元素
+            const dialogContent = dialogElement.$el.querySelector('.el-dialog');
+            if (!dialogContent) {
+              return;
+            }
+            const dialogRect = dialogContent.getBoundingClientRect();
+            const dialogWidth = dialogRect.width;
+            const dialogHeight = dialogRect.height;
+            const padding = 10; // 安全边距
+
+            const clickX = event.clientX;
+            const clickY = event.clientY;
+
+            const windowWidth = window.innerWidth;
+            const windowHeight = window.innerHeight;
+
+            // 水平定位 - 中心对齐
+            let left = clickX - dialogWidth / 2;
+            // 边界检查
+            left = Math.max(padding, Math.min(left, windowWidth - dialogWidth - padding));
+
+            // 垂直定位 - 点击位置作为下边界中心
+            let top = clickY - dialogHeight;
+            // 上方空间不足时,改为向下展开
+            if (top < padding) {
+              top = clickY + padding;
+              // 如果向下展开会超出屏幕,则贴底部显示
+              if (top + dialogHeight > windowHeight - padding) {
+                top = windowHeight - dialogHeight - padding;
+              }
+            }
+
+            this.dialogStyle = {
+              position: 'fixed',
+              top: `${top - 20}px`,
+              left: `${left}px`,
+              margin: '0',
+              transform: 'none',
+            };
+          });
+        }
+        return;
+      } // 如果是预览模式,不操作
       if (item) {
         this.visible = true;
         this.selectContent = item;

+ 28 - 4
src/views/book/courseware/create/components/base/common/CorrectPinyin.vue

@@ -1,8 +1,8 @@
 <template>
   <el-dialog
     :visible="visible"
-    width="400px"
-    top="38vh"
+    width="550px"
+    top="20vh"
     :show-close="false"
     :close-on-click-modal="false"
     @close="dialogClose"
@@ -36,6 +36,14 @@
     <span class="content-text" :style="dataContent.activeTextStyle">{{ dataContent.text }}</span>
     <el-input v-model="numberPinyin" autocomplete="off" placeholder="请输入正确的拼音" @blur="convertTonePinyin" />
     <span class="tips">一到四声分别用数字1-4表示,轻声用0表示,拼音间用空格隔开。</span>
+    <RichText
+      v-model="dataContent.note"
+      toolbar="fontselect fontsizeselect forecolor backcolor|underline|bold italic strikethrough alignleft aligncenter alignright"
+      :wordlimit-num="false"
+      :height="200"
+      placeholder="输入注释"
+      page-from="audit"
+    />
     <template slot="footer">
       <el-button size="medium" @click="dialogClose">取消</el-button>
       <el-button type="primary" size="medium" @click="confirm">保存</el-button>
@@ -45,10 +53,14 @@
 
 <script>
 import { addTone, handleToneValue } from '@/utils/common';
+import RichText from '@/components/RichText.vue';
 import _ from 'lodash';
 
 export default {
   name: 'CorrectPinyin',
+  components: {
+    RichText,
+  },
   props: {
     visible: {
       type: Boolean,
@@ -104,7 +116,7 @@ export default {
         top: 0,
         left: 0,
       },
-      dataContent: { activeTextStyle: {} },
+      dataContent: { activeTextStyle: {}, note: {} },
     };
   },
   watch: {
@@ -144,6 +156,13 @@ export default {
       this.numberPinyin = '';
     },
     confirm() {
+      if (this.dataContent.note) {
+        this.$set(this.dataContent.activeTextStyle, 'backgroundColor', '#fff3b7');
+        this.$set(this.dataContent.activeTextStyle, 'cursor', 'pointer');
+      } else {
+        this.$set(this.dataContent.activeTextStyle, 'backgroundColor', '');
+        this.$set(this.dataContent.activeTextStyle, 'cursor', 'text');
+      }
       this.$emit('update:visible', false);
       this.$emit('fillTonePinyin', this.dataContent);
       this.numberPinyin = '';
@@ -208,7 +227,7 @@ export default {
     flex-direction: column;
     row-gap: 8px;
     align-items: center;
-    padding: 24px;
+    padding-bottom: 10px;
 
     .tone-pinyin {
       font-family: 'League';
@@ -221,10 +240,15 @@ export default {
     }
 
     .tips {
+      width: 100%;
       font-size: 12px;
       color: #a0a0a0;
       text-align: left;
     }
+
+    .rich-wrapper {
+      width: 100%;
+    }
   }
 
   :deep &__footer {

+ 2 - 1
src/views/book/courseware/create/components/base/rich_text/RichText.vue

@@ -161,7 +161,7 @@ export default {
       return hash.toString(36); // 转为base36缩短长度
     },
     // 填充校对后的拼音
-    fillCorrectPinyin({ selectContent: { text, pinyin, activeTextStyle }, i, j, k }) {
+    fillCorrectPinyin({ selectContent: { text, pinyin, activeTextStyle, note }, i, j, k }) {
       this.data.paragraph_list_parameter.pinyin_proofread_word_list.push({
         paragraph_index: i,
         sentence_index: j,
@@ -171,6 +171,7 @@ export default {
       });
       if (pinyin) this.data.paragraph_list[i][j][k].pinyin = pinyin;
       if (activeTextStyle) this.data.paragraph_list[i][j][k].activeTextStyle = activeTextStyle;
+      if (note) this.data.paragraph_list[i][j][k].note = note;
     },
     // 思维导图
     handlerMindMap() {