hoj-vue 專案中的編輯器支援latex公式

童心小城發表於2024-04-15

Editor.vue:

  1 <template>
  2   <div class="mavonEditor">
  3     <mavon-editor
  4       ref="md"
  5       @imgAdd="$imgAdd"
  6       @imgDel="$imgDel"
  7       :ishljs="true"
  8       :html="openHtml"
  9       :autofocus="false"
 10       :toolbars="toolbars"
 11       v-model="currentValue"
 12       codeStyle="arduino-light"
 13       @save="saveMavon"
 14     >
 15       <template v-slot:left-toolbar-after v-if="isAdminRole">
 16         <button
 17           type="button"
 18           :title="$t('m.Upload_file')"
 19           class="op-icon fa markdown-upload"
 20           aria-hidden="true"
 21           @click="uploadFile"
 22         >
 23           <!-- 這裡用的是element-ui給出的圖示 -->
 24           <i class="el-icon-upload" />
 25         </button>
 26       </template>
 27       <!-- <template slot="right-toolbar-after" v-if="isAdminRole">
 28         <button
 29           type="button"
 30           :title="$t('m.Upload_file')"
 31           class="op-icon fa markdown-upload"
 32           aria-hidden="true"
 33           @click="uploadFile"
 34         >
 35           <i class="el-icon-upload" />
 36           插入數學公式
 37         </button>
 38       </template> -->
 39     </mavon-editor>
 40     <!-- 在這裡放一個隱藏的input,用來選擇檔案 -->
 41     <input
 42       ref="uploadInput"
 43       style="display: none"
 44       type="file"
 45       @change="uploadFileChange"
 46     />
 47   </div>
 48 </template>
 49 <script>
 50 import { mapGetters } from "vuex";
 51 import { addCodeBtn } from "@/common/codeblock";
 52 export default {
 53   name: "Editor",
 54   props: {
 55     value: {
 56       type: String,
 57       default: "",
 58     },
 59     openHtml: {
 60       type: Boolean,
 61       default: true,
 62     },
 63   },
 64   data() {
 65     return {
 66       currentValue: this.value,
 67       img_file: {},
 68       toolbars: {
 69         bold: true, // 粗體
 70         italic: true, // 斜體
 71         header: true, // 標題
 72         underline: true, // 下劃線
 73         strikethrough: true, // 中劃線
 74         mark: true, // 標記
 75         superscript: true, // 上角標
 76         subscript: true, // 下角標
 77         quote: true, // 引用
 78         ol: true, // 有序列表
 79         ul: true, // 無序列表
 80         link: true, // 連結
 81         imagelink: false, // 圖片連結
 82         code: true, // code
 83         table: true, // 表格
 84         fullscreen: true, // 全屏編輯
 85         readmodel: true, // 沉浸式閱讀
 86         htmlcode: true, // 展示html原始碼
 87         help: true, // 幫助
 88         /* 1.3.5 */
 89         undo: true, // 上一步
 90         redo: true, // 下一步
 91         trash: true, // 清空
 92         save: true, // 儲存(觸發events中的save事件)
 93         /* 1.4.2 */
 94         navigation: true, // 導航目錄
 95         /* 2.1.8 */
 96         alignleft: true, // 左對齊
 97         aligncenter: true, // 居中
 98         alignright: true, // 右對齊
 99         /* 2.2.1 */
100         subfield: true, // 單雙欄模式
101         preview: true, // 預覽
102       },
103       s_external_link: {
104         katex_js: function () {
105           return "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/katex.min.js";
106         },
107         katex_css: function () {
108           return "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/katex.min.css";
109         },
110       },
111     };
112   },
113   created() {
114     if (this.isAdminRole || this.isGroupAdmin) {
115       this.toolbars.imagelink = true;
116     }
117   },
118   mounted() {
119     console.log("值1", this.openHtml);
120     console.log("值2", this.currentValue);
121   },
122   methods: {
123     saveMavon(value, render) {
124       console.log("this is render" + render);
125       console.log("this is value" + value);
126     },
127     loadExternalLink(name, type, callback) {
128       if (typeof this.p_external_link[name] !== "function") {
129         if (this.p_external_link[name] !== false) {
130           console.error(
131             "external_link." + name,
132             "is not a function, if you want to disabled this error log, set external_link." +
133               name,
134             "to function or false"
135           );
136         }
137         return;
138       }
139       var _obj = {
140         css: loadLink,
141         js: loadScript,
142       };
143       if (_obj.hasOwnProperty(type)) {
144         _obj[type](this.p_external_link[name](), callback);
145       }
146     },
147     $paste($e) {
148       var clipboardData = $e.clipboardData;
149       if (clipboardData) {
150         var items = clipboardData.items;
151         if (!items) return;
152         var types = clipboardData.types || [];
153         var item = null;
154         for (var i = 0; i < types.length; i++) {
155           if (types[i] === "Files") {
156             item = items[i];
157             break;
158           }
159         }
160         if (item && item.kind === "file") {
161           stopEvent($e);
162           var oFile = item.getAsFile();
163         }
164       }
165     },
166     initExternalFuc() {
167       var $vm = this;
168       var _external_ = [
169       ];
170       var _type_ = typeof $vm.externalLink;
171       var _is_object = _type_ === "object";
172       var _is_boolean = _type_ === "boolean";
173       for (var i = 0; i < _external_.length; i++) {
174         if (
175           (_is_boolean && !$vm.externalLink) ||
176           (_is_object && $vm.externalLink[_external_[i]] === false)
177         ) {
178           $vm.p_external_link[_external_[i]] = false;
179         } else if (
180           _is_object &&
181           typeof $vm.externalLink[_external_[i]] === "function"
182         ) {
183           $vm.p_external_link[_external_[i]] = $vm.externalLink[_external_[i]];
184         } else {
185           $vm.p_external_link[_external_[i]] =
186             $vm.s_external_link[_external_[i]];
187         }
188       }
189     },
190     // 編輯開關
191     editableTextarea() {
192       let text_dom = this.$refs.vNoteTextarea.$refs.vTextarea;
193       if (this.editable) {
194         text_dom.removeAttribute("disabled");
195       } else {
196         text_dom.setAttribute("disabled", "disabled");
197       }
198     },
199     // 將圖片上傳到伺服器,返回地址替換到md中
200     $imgAdd(pos, $file) {
201       if (!this.isAdminRole && !this.isGroupAdmin) {
202         return;
203       }
204       var formdata = new FormData();
205       formdata.append("image", $file);
206       let gid = this.$route.params.groupID;
207       if (gid != null && gid != undefined) {
208         formdata.append("gid", gid);
209       }
210       //將下面上傳介面替換為你自己的伺服器介面
211       this.$http({
212         url: "介面1",
213         method: "post",
214         data: formdata,
215         headers: { "Content-Type": "multipart/form-data" },
216       }).then((res) => {
217         this.$refs.md.$img2Url(pos, res.data.data.link);
218         this.img_file[res.data.data.link] = res.data.data.fileId;
219       });
220     },
221     $imgDel(pos) {
222       // 刪除檔案
223       this.$http({
224         url: "介面2",
225         method: "get",
226         params: {
227           fileId: this.img_file[pos[0]],
228         },
229       });
230     },
231     uploadFile() {
232       // 透過ref找到隱藏的input標籤,觸發它的點選方法
233       this.$refs.uploadInput.click();
234     },
235     // 監聽input獲取檔案的狀態
236     uploadFileChange(e) {
237       // 獲取到input選取的檔案
238       const file = e.target.files[0];
239       // 建立form格式的資料,將檔案放入form中
240       const formdata = new FormData();
241       formdata.append("file", file);
242       let gid = this.$route.params.groupID;
243       if (gid != null && gid != undefined) {
244         formdata.append("gid", gid);
245       }
246       this.$http({
247         url: "介面3",
248         method: "post",
249         data: formdata,
250         headers: { "Content-Type": "multipart/form-data" },
251       }).then((res) => {
252         // 這裡獲取到的是mavon編輯器例項,上面掛載著很多方法
253         const $vm = this.$refs.md;
254         // 將檔名與檔案路徑插入當前游標位置,這是mavon-editor 內建的方法
255         $vm.insertText($vm.getTextareaDom(), {
256           prefix: `[${file.name}](${res.data.data.link})`,
257           subfix: "",
258           str: "",
259         });
260       });
261     },
262   },
263   computed: {
264     ...mapGetters(["isAdminRole", "isGroupAdmin"]),
265   },
266   watch: {
267     value(val) {
268       if (this.currentValue !== val) {
269         this.currentValue = val;
270       }
271     },
272     currentValue(newVal, oldVal) {
273       if (newVal !== oldVal) {
274         this.$emit("update:value", newVal);
275         this.$nextTick((_) => {
276           addCodeBtn();
277         });
278       }
279     },
280     isAdminRole(val) {
281       if (!val) {
282         this.toolbars.imagelink = false;
283       } else {
284         this.toolbars.imagelink = true;
285       }
286     },
287     editable: function () {
288       this.editableTextarea();
289     },
290   },
291 };
292 </script>
293 <style>
294 .auto-textarea-wrapper .auto-textarea-block {
295   white-space: pre-wrap !important;
296 }
297 </style>

相關文章