基於VUE接入TinyMCE富文本編輯器 漂亮簡潔 封裝成組件隨用隨調

来源:https://www.cnblogs.com/iwillrich/archive/2022/06/23/16404236.html
-Advertisement-
Play Games

TinyMCE是一款易用、且功能強大的所見即所得的富文本編輯器。同類程式有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 配置靈活,界面簡潔,支持自定義插件。 TinyMCE中文手冊:http://tinymc ...


TinyMCE是一款易用、且功能強大的所見即所得的富文本編輯器。同類程式有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。
配置靈活,界面簡潔,支持自定義插件。
TinyMCE中文手冊:http://tinymce.ax-z.cn

一、安裝環境

1、安裝需要的包

我使用的是v5版本的,需要搭配tinymce-vue包來使用

npm install tinymce/[email protected]

npm install [email protected]

2、將安裝的tinymce包copy放在public下

cp ./node_modules/tinymce ./public/tinymce 

3、下載語言包

打開鏈接 http://tinymce.ax-z.cn/static/tiny/langs/zh_CN.js ,將zh_CN.js 另存到 public/tinymce/langs下,沒有langs文件夾的可以手動創建一下

二、代碼引用

附上我封裝的組件,可以作為參考,上傳文件的邏輯需要結合業務邏輯自行實現

<template>
	<Editor :id="editorId" v-if="reFresh" v-model="newData" :init="init" />
</template>
<script>
// 引入組件
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import 'tinymce/themes/silver/theme'
import "tinymce/skins/ui/oxide/skin.min.css";

// 引入富文本編輯器主題的js和css

export default {
	name: "TinymceEditor",
	components: { Editor },
	data() {
		return {
			init: {    // 配置文件
				base_url: '/tinymce',
				menubar: false,
				external_plugins: {  //引入需要的插件
					anchor: "/tinymce/plugins/anchor/plugin.min.js",
					code: "/tinymce/plugins/code/plugin.min.js",
					print: "/tinymce/plugins/print/plugin.min.js",
					preview: "/tinymce/plugins/preview/plugin.min.js",
					searchreplace: "/tinymce/plugins/searchreplace/plugin.min.js",
					autolink: "/tinymce/plugins/autolink/plugin.min.js",
					directionality: "/tinymce/plugins/directionality/plugin.min.js",
					visualblocks: "/tinymce/plugins/visualblocks/plugin.min.js",
					visualchars: "/tinymce/plugins/visualchars/plugin.min.js",
					fullscreen: "/tinymce/plugins/fullscreen/plugin.min.js",
					image: "/tinymce/plugins/image/plugin.min.js",
					link: "/tinymce/plugins/link/plugin.min.js",
					media: "/tinymce/plugins/media/plugin.min.js",
					template: "/tinymce/plugins/template/plugin.min.js",
					codesample: "/tinymce/plugins/codesample/plugin.min.js",
					table: "/tinymce/plugins/table/plugin.min.js",
					charmap: "/tinymce/plugins/charmap/plugin.min.js",
					pagebreak: "/tinymce/plugins/pagebreak/plugin.min.js",
					nonbreaking: "/tinymce/plugins/nonbreaking/plugin.min.js",
					insertdatetime: "/tinymce/plugins/insertdatetime/plugin.min.js",
					advlist: "/tinymce/plugins/advlist/plugin.min.js",
					lists: "/tinymce/plugins/lists/plugin.min.js",
					wordcount: "/tinymce/plugins/wordcount/plugin.min.js",
					imagetools: "/tinymce/plugins/imagetools/plugin.min.js",
					textpattern: "/tinymce/plugins/textpattern/plugin.min.js",
					help: "/tinymce/plugins/help/plugin.min.js",
					emoticons: "/tinymce/plugins/emoticons/plugin.min.js",
					autosave: "/tinymce/plugins/autosave/plugin.min.js",
					iframe: "/tinymce/plugins/iframe/plugin.min.js",
					hr: "/tinymce/plugins/hr/plugin.min.js",
					// formatpainter: "/tinymce/plugins/formatpainter/plugin.min.js",
				},
				language_url:
					"/tinymce/langs/zh_CN.js",   //語言文件
				language: "zh_CN",
				font_formats:
					"微軟雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;蘋果蘋方=PingFang SC,Microsoft YaHei,sans-serif;宋體=simsun,serif;仿宋體=FangSong,serif;黑體=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;",

				toolbar: [
					"template code undo redo restoredraft cut copy paste pastetext forecolor backcolor bold italic underline strikethrough link unlink anchor alignleft aligncenter alignright alignjustify outdent indent formatselect fontselect fontsizeselect bullist numlist blockquote subscript superscript removeformat table image media charmap emoticons pagebreak insertdatetime print preview fullscreen formatpainter iframe hr",
				],
				templates: [],
				// content_css : ['/layui/css/layui.css','/css/public.css?v=1'],
				content_css: [],
				height: 800, //編輯器高度
				min_height: 200,
				max_height: 600,
				branding: false,
				paste_data_images: true, // 允許粘貼圖像
				file_picker_types: "file image media",
				images_upload_handler: (blobInfo, success) => {
					var xhr, formData;
					var file = blobInfo.blob();//轉化為易於理解的file對象
					xhr = new XMLHttpRequest();
					xhr.withCredentials = false;
					xhr.open("POST",`${process.env.VUE_APP_API_URL}/file/upload`);  //上傳文件的地址,需要替換成自己的
					xhr.setRequestHeader("Authorization",window.localStorage.escourse_token);
					xhr.onload = function () {
						var json;
						json = JSON.parse(xhr.responseText);
					};
					formData = new FormData();
					formData.append("file", file, file.name);
					formData.append("module", "post");
					formData.append("file_source", "1");
					// formData.append("token",window.localStorage.escourse_token)
					xhr.send(formData);
					xhr.onload = function () {
							let json = JSON.parse(xhr.responseText);
							if (json.code == 200) {
								success(process.env.VUE_APP_API_URL + json.data.file.path)
								return
							}
						};
				},
				media_url_resolver: function (data, resolve) {
					try {
						let videoUri = encodeURI(data.url);
						let embedHtml = `<p>
                            <span
                                class="mce-object mce-object-video"
                                data-mce-selected="1"
                                data-mce-object="video"
                                data-mce-p-width="100%"
                                data-mce-p-height="auto"
                                data-mce-p-controls="controls"
                                data-mce-p-controlslist="nodownload"
                                data-mce-p-allowfullscreen="true"
                                data-mce-p-src=${videoUri} >
                                <video src=${data.url} width="100%" height="auto" controls="controls" controlslist="nodownload">
                                </video>
                            </span>
                            </p>
                            <p style="text-align: left;"></p>`;
						resolve({ html: embedHtml });
					} catch (e) {
						resolve({ html: "" });
					}
				},
				file_picker_callback(cb, value, meta) {
					//當點擊meidia圖標上傳時,判斷meta.filetype == 'media'有必要,因為file_picker_callback是media(媒體)、image(圖片)、file(文件)的共同入口
					console.log('meta', meta);

					//創建一個隱藏的type=file的文件選擇input
					let input = document.createElement("input");
					input.setAttribute("type", "file");
					input.onchange = function () {
						var xhr, formData;
						let file = this.files[0]; //只選取第一個文件。如果要選取全部,後面註意做修改
						xhr = new XMLHttpRequest();
						xhr.withCredentials = false;
						xhr.open("POST",`${process.env.VUE_APP_API_URL}/file/upload`);   //上傳文件的地址,需要替換成自己的
						xhr.setRequestHeader("Authorization",window.localStorage.escourse_token);
						xhr.onload = function () {
							var json;
							json = JSON.parse(xhr.responseText);
						};
						formData = new FormData();
						formData.append("file", file, file.name);
						formData.append("module", "post");
						formData.append("file_source", "1");
						// formData.append("token",window.localStorage.escourse_token)
						xhr.send(formData);

						xhr.upload.onprogress = function () {
							// 進度(e.loaded / e.total * 100)
							// progress(e.loaded / e.total * 100);
						};
						// xhr.onerror = function () {
						//   //根據自己的需要添加代碼
						//   console.log(xhr.status);
						//   return;
						// };
						xhr.onload = function () {
							let json = JSON.parse(xhr.responseText);
							if (json.code == 200) {
								// if (meta.filetype == "media") {
									cb(process.env.VUE_APP_API_URL + json.data.file.path, { text: json.data.file.name })
								// }
								// if (meta.filetype == "file") {
								// 	cb(json.data.url, { text: json.data.name })
								// }
								return
							}
						};
					};
					//觸發點擊
					input.click();

				},
			},
			editorId: this.id,
			newData: this.data,
			reFresh:false,
			laws:[]
		};
	},
    created(){
        this.getBandData();
    },
	mounted() {
		tinymce.init({});
	},
	props: {
		data: String,
		onChange: Function   // 回調函數,將新修改的內容做為第一個參數返回
	},
	watch: {
		newData: 'updateData',
		data: 'uptData',
		"init.templates"(newValue) {   // 如果配置發生改變,則刷新編輯器
			this.reFresh = false;
			this.$nextTick(() => {
				this.reFresh = true;
			});
		},
	},
	methods: {
		async getBandData() { 
			let urlResult = await getTemplates();  //這邊我是自己封裝了個插件,通過getTemplates介面,從後端讀取模板,再顯示到編輯器插件上,如果不需要可以刪掉
			if (urlResult.code == 200) {
				let array = urlResult.data;
				let arr = [];
				for (let index = 0; index < array.length; index++) {
					const element = array[index];
					let data = {
						title: element.name,
						content: element.html,
						description: element.html,
						// description: "<img src='"+this.urlConfig.adminUrl + element.thumbnail+"'/>",
					};
					arr.push(data);
				}
				this.laws = arr; //得到的數據賦值給laws
				this.init.templates = this.laws;
			}
		},
		addContent(html) {
			console.log('html', html)
			this.newData = this.newData + html
		},
		uptData() {
			this.newData = JSON.parse(JSON.stringify(this.data))
		},
		updateData() {
			this.onChange(this.newData)
		},
		clear() {
			this.editorValue = "";
		},
	},
};
</script>

在頁面中引用

<template>
	<Editor v-bind:data="data" :onChange="onchange"></Editor>
</template>
import Editor from "../../../components/tinymce/editor";
export default {
	data() {
		return {
			data:''
		};
	},
	components: {
		Editor
	},
	methods: {
		onchange(e){
			this.data = e
		}
	}
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題導入 在之前項目的基礎功能實現中,後臺管理和移動端在進行數據訪問的時候,都是直接操作資料庫MySQL。此時的系統有且僅有一臺MySQL伺服器,則可能會出現如下問題 ①、讀和寫所有壓力都由一臺資料庫承擔,壓力大 ②、資料庫伺服器磁碟損壞導致數據丟失,單點故障 解決方案 很簡單,一臺伺服器撐不住,那 ...
  • 資料庫設計的設計內容包括:需求分析、概念結構設計、邏輯結構設計、物理結構設計、資料庫的實施和資料庫的運行和維護。 ...
  • 原文鏈接:走好數據中台最後一公裡,數據服務API是數據中台的標配 視頻回顧:點擊這裡 課件獲取:點擊這裡 一、數據服務API建設背景 在數字化轉型的時代背景下,新需求的大量增長、新技術的不斷迭代,“互聯網化、數字化”進程的不斷深入,越來越多的業務被遷移到互聯網上,產生大量的業務交互和對外服務需求,對 ...
  • E-R圖也稱實體-聯繫圖(Entity Relationship Diagram),它提供了表示實體類型、屬性和聯繫的方法,用來描述現實世界的概念模型。 ...
  • Flutter IOS 鍵盤焦點 關閉打開鍵盤 Android 的TextField 獲取焦點打開的鍵盤中有關閉鍵盤的箭頭 可以進行關閉鍵盤 IOS 則就不行,它的鍵盤沒有關閉鍵盤的按鈕 就很噁心!! IOS 的輸入框在你輸入完數據之後, 你就沒辦法關閉鍵盤。 雖然你可以滑動界面顯示被隱藏的按鈕,也 ...
  • 隨著信息化的發展,很多具有重要價值的知識隱藏分佈在海量數據中,影響了人們獲取知識的效率,如何處理繁雜的非結構化文本數據成為難題。 近日,HMS Core機器學習服務6.5.0版本新增線上文本實體抽取能力,該能力可以檢測出文本中是否存在比如日期、姓名、專有名詞等實體信息,並將此類實體抽取出來,即自動處 ...
  • vue2中只能有一個根標簽,但是在vue3中根組件已經可以有多個根節點了 在vue2中只所以這麼做是因為vdom是一顆單根樹形結構,patch方法在遍歷的時候從根節點開始遍歷,它要求只有一個根節點,組件也會轉換為一個vdom,自然滿足這個要求 vue3中值所以可以有多個節點,是因為引入了Fragme ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 防抖節流 這也是一個經典題目了,首先要知道什麼是防抖,什麼是節流。 防抖: 在一段時間內,事件只會最後觸發一次。 節流: 事件,按照一段時間的間隔來進行觸發。 實在不懂的話,可以去這個大佬的Demo地址玩玩防抖節流DEMO // 防 ...
一周排行
    -Advertisement-
    Play Games
  • 最近做項目過程中,使用到了海康相機,官方只提供了C/C++的SDK,沒有搜尋到一個合適的封裝了的C#庫,故自己動手,簡單的封裝了一下,方便大家也方便自己使用和二次開發 ...
  • 前言 MediatR 是 .NET 下的一個實現消息傳遞的庫,輕量級、簡潔高效,用於實現進程內的消息傳遞機制。它基於中介者設計模式,支持請求/響應、命令、查詢、通知和事件等多種消息傳遞模式。通過泛型支持,MediatR 可以智能地調度不同類型的消息,非常適合用於領域事件處理。 在本文中,將通過一個簡 ...
  • 前言 今天給大家推薦一個超實用的開源項目《.NET 7 + Vue 許可權管理系統 小白快速上手》,DncZeus的願景就是做一個.NET 領域小白也能上手的簡易、通用的後臺許可權管理模板系統基礎框架。 不管你是技術小白還是技術大佬或者是不懂前端Vue 的新手,這個項目可以快速上手讓我們從0到1,搭建自 ...
  • 第1章:WPF概述 本章目標 瞭解Windows圖形演化 瞭解WPF高級API 瞭解解析度無關性概念 瞭解WPF體繫結構 瞭解WPF 4.5 WPF概述 ​ 歡迎使用 Windows Presentation Foundation (WPF) 桌面指南,這是一個與解析度無關的 UI 框架,使用基於矢 ...
  • 在日常開發中,並不是所有的功能都是用戶可見的,還在一些背後默默支持的程式,這些程式通常以服務的形式出現,統稱為輔助角色服務。今天以一個簡單的小例子,簡述基於.NET開發輔助角色服務的相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 第3章:佈局 本章目標 理解佈局的原則 理解佈局的過程 理解佈局的容器 掌握各類佈局容器的運用 理解 WPF 中的佈局 WPF 佈局原則 ​ WPF 視窗只能包含單個元素。為在WPF 視窗中放置多個元素並創建更貼近實用的用戶男面,需要在視窗上放置一個容器,然後在這個容器中添加其他元素。造成這一限制的 ...
  • 前言 在平時項目開發中,定時任務調度是一項重要的功能,廣泛應用於後臺作業、計劃任務和自動化腳本等模塊。 FreeScheduler 是一款輕量級且功能強大的定時任務調度庫,它支持臨時的延時任務和重覆迴圈任務(可持久化),能夠按秒、每天/每周/每月固定時間或自定義間隔執行(CRON 表達式)。 此外 ...
  • 目錄Blazor 組件基礎路由導航參數組件參數路由參數生命周期事件狀態更改組件事件 Blazor 組件 基礎 新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項: 客戶端組件 (Auto/WebAssembly): 最終解決方案裡面會有兩個項目:伺服器 ...
  • 先看一下效果吧: isChecked = false 的時候的效果 isChecked = true 的時候的效果 然後我們來實現一下這個效果吧 第一步:創建一個空的wpf項目; 第二步:在項目裡面添加一個checkbox <Grid> <CheckBox HorizontalAlignment=" ...
  • 在編寫上位機軟體時,需要經常處理命令拼接與其他設備進行通信,通常對不同的命令封裝成不同的方法,擴展稍許麻煩。 本次擬以特性方式實現,以兼顧維護性與擴展性。 思想: 一種命令對應一個類,其類中的各個屬性對應各個命令段,通過特性的方式,實現其在這包數據命令中的位置、大端或小端及其轉換為對應的目標類型; ...