فهرست منبع

富文本扩展公式

zq 2 ماه پیش
والد
کامیت
779d70c6e3
1فایلهای تغییر یافته به همراه124 افزوده شده و 2 حذف شده
  1. 124 2
      src/components/RichText.vue

+ 124 - 2
src/components/RichText.vue

@@ -18,6 +18,30 @@
       <SvgIcon icon-class="close-circle" size="16" @click="deleteFill" />
       <span class="button" @click="deleteFill">删除填空</span>
     </div>
+
+    <el-dialog
+      title="输入公式"
+      :visible.sync="isViewMathDialog"
+      width="400px"
+      top="20vh"
+      :show-close="false"
+      :close-on-press-escape="false"
+      :close-on-click-modal="false"
+      @close="isViewMathDialog = false"
+    >
+      <el-input
+        v-model="mathData.math"
+        type="textarea"
+        :autosize="{ minRows: 4, maxRows: 8 }"
+        resize="none"
+        placeholder="请输入公式"
+      />
+      <div ref="mathContainer" v-data-h="mathData.math" class="formula-render" v-html="mathData.math"></div>
+      <template slot="footer">
+        <el-button size="medium" @click="isViewMathDialog = false">取消</el-button>
+        <el-button type="primary" size="medium" @click="mathConfirm">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -50,6 +74,7 @@ import { getRandomNumber } from '@/utils';
 import { isNodeType } from '@/utils/validate';
 import { fileUpload } from '@/api/app';
 import { addTone, handleToneValue } from '@/utils/common';
+import { getMathData } from '@/views/book/courseware/data/math';
 
 export default {
   name: 'RichText',
@@ -82,7 +107,7 @@ export default {
       type: [String, Boolean],
       /* eslint-disable max-len */
       default:
-        'fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright | bullist numlist | image media | link blockquote hr',
+        'fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright | bullist numlist | image media | link blockquote hr mathjax',
     },
     wordlimitNum: {
       type: [Number, Boolean],
@@ -107,6 +132,11 @@ export default {
   },
   data() {
     return {
+      mathData: getMathData(),
+      isViewMathDialog: false,
+      dialogMathValidate: false,
+      mathEleIsInit: true,
+      math: '',
       isShow: false,
       contentmenu: {
         top: 0,
@@ -114,6 +144,14 @@ export default {
       },
       id: getRandomNumber(),
       init: {
+        content_style: `
+          mjx-container, mjx-container * {
+            font-size: 16px !important; /* 强制固定字体 */
+            line-height: 1.2 !important; /* 避免行高影响 */
+          } `, // 解决公式每点击一次字体就变大
+        valid_elements: '*[*]', // 允许所有标签和属性
+        valid_children: '+body[style]', // 允许 MathJax 的样式
+        extended_valid_elements: 'span[*],mjx-container[*],svg[*],path[*]', // 明确允许 MathJax 标签
         inline: this.inline,
         font_size: this.fontSize,
         language_url: `${process.env.BASE_URL}tinymce/langs/zh_CN.js`,
@@ -130,7 +168,8 @@ export default {
         menubar: false, // 菜单栏
         branding: false, // 品牌
         statusbar: false, // 状态栏
-        setup(editor) {
+        setup: (editor) => {
+          let isRendered = false; // 标记是否已渲染
           editor.on('init', () => {
             editor.getBody().style.fontSize = `${this.font_size}pt`; // 设置默认字体大小
             editor.getBody().style.fontFamily = 'Arial'; // 设置默认字体
@@ -140,7 +179,23 @@ export default {
             if (editor?.queryCommandState('ToggleToolbarDrawer')) {
               editor.execCommand('ToggleToolbarDrawer');
             }
+            if (!isRendered && window.MathJax) {
+              isRendered = true;
+              window.MathJax.typesetPromise([editor.getBody()]);
+            }
+          });
+
+          // 添加 MathJax 按钮
+          editor.ui.registry.addButton('mathjax', {
+            text: '∑',
+            tooltip: '插入公式',
+            onAction: () => {
+              this.isViewMathDialog = true;
+            },
           });
+
+          // 内容变化时重新渲染公式
+          editor.on('change', () => this.renderMath());
         },
         font_formats:
           '楷体=楷体,微软雅黑;' +
@@ -174,6 +229,21 @@ export default {
       },
     };
   },
+  watch: {
+    // 监听数据变化,重新渲染 MathJax
+    'mathData.math': {
+      handler(n, o) {
+        if (n) this.renderMathDialog();
+      },
+      deep: true,
+    },
+    isViewMathDialog: {
+      handler() {
+        this.dialogMathValidate = false;
+        this.mathData.math = '';
+      },
+    },
+  },
   created() {
     window.addEventListener('click', this.hideToolbarDrawer);
     if (this.isFill) {
@@ -464,6 +534,54 @@ export default {
         top: `${pixelsFromTop - 18}px`,
       };
     },
+
+    mathConfirm() {
+      if (!this.dialogMathValidate) return;
+
+      let editor = tinymce.get(this.id);
+      let tmpId = getRandomNumber();
+      let tmpMathData = this.mathData.math;
+      editor.insertContent(`
+         <span id="${tmpId}" contenteditable="false" class="mathjax-container editor-math">
+           ${tmpMathData}
+         </span>
+       `);
+      // editor.insertContent(`${latex}`);
+      this.mathEleIsInit = false;
+      this.renderMath(tmpId);
+      this.isViewMathDialog = false;
+    },
+    // 渲染公式
+    async renderMath(id) {
+      if (this.mathEleIsInit) return; // 如果公式已经渲染过,返回
+      if (window.MathJax) {
+        let editor = tinymce.get(this.id);
+        let eleMathArs = [];
+        if (id) {
+          // 插入的时候,会传递ID,执行单个渲染
+          let ele = editor.dom.select(`#${id}`)[0];
+          eleMathArs = [ele];
+        } else {
+          // 否则,查询编辑器里面所有的公式
+          eleMathArs = editor.dom.select(`.editor_math`);
+        }
+        if (eleMathArs.length === 0) return;
+        await this.$nextTick();
+        window.MathJax.typesetPromise(eleMathArs).catch((err) => console.error('MathJax error:', err));
+        this.mathEleIsInit = true;
+      }
+    },
+    async renderMathDialog() {
+      this.dialogMathValidate = false;
+      await this.$nextTick();
+      try {
+        await window.MathJax.typesetPromise([this.$refs.mathContainer]);
+        let mathRes = this.$refs.mathContainer.innerHTML;
+        if (mathRes && mathRes.indexOf('merror') === -1) this.dialogMathValidate = true;
+      } catch (err) {
+        console.error('公式渲染失败:', err);
+      }
+    },
   },
 };
 </script>
@@ -524,4 +642,8 @@ export default {
     margin: 0 4px;
   }
 }
+
+mjx-container {
+  font-size: 16px !important;
+}
 </style>