Vue 中使用 Tinymce 富文本编辑器
生活随笔
收集整理的這篇文章主要介紹了
Vue 中使用 Tinymce 富文本编辑器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
參考鏈接:https://www.cnblogs.com/wisewrong/p/8985471.html
Tinymce : 從 word 粘貼過來還能保持絕大部分格式的編輯器
一. 下載
?
npm install tinymce -S?安裝之后,在 node_modules 中找到 tinymce/skins 目錄,然后將 skins 目錄拷貝到 public 目錄下
(如果是使用 vue-cli 2.x 構建的 typescript 項目,就放到 static 目錄下)
tinymce 默認是英文界面,所以還需要下載一個中文 語言包
將這個語言包放到 public 目錄下,為了結構清晰,我包了一層 tinymce 目錄
二. 初始化
import tinymce from 'tinymce/tinymce' // 初始化發現編輯器不顯示,報“theme.js:1 Uncaught SyntaxError: Unexpected token <”這個錯 // 需要手動引入tinymce主題,在init({})方法里加theme: 'silver',沒用。 import 'tinymce/themes/silver/theme' cnpm install --save tinymce/theme三. 使用示例
<template><div name='tinymce'><div class='title'><input placeholder="請輸入文章標題" v-model="title"/></div><div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container"><textarea :id="tinymceId" class="tinymce-textarea"/><div class="editor-custom-btn-container"><editorImage color="var(--main-color)" class="editor-upload-btn" @successCBK="imageSuccessCBK" /></div></div><div class="publish"><span @click="submission">提交</span><span class="cancel">取消</span></div></div> </template><script> import plainBtn from '@/components/Buttons/plainBtn'import tinymce from 'tinymce/tinymce' // import 'tinymce/themes/mobile/theme' // import 'tinymce/themes/modern/theme' // 按示例初始化發現編輯器不顯示,報“theme.js:1 Uncaught SyntaxError: Unexpected token <”這個錯 // 需要手動引入tinymce主題,在init({})方法里加theme: 'silver',沒用。 import 'tinymce/themes/silver/theme'import editorImage from './components/editorImage' import plugins from './plugins' import toolbar from './toolbar'export default {name: 'Tinymce',components: { editorImage,plainBtn },props: {id: {type: String,default: function() {return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')}},value: {type: String,default: 'Editor me ...'},toolbar: {type: Array,required: false,default() {return []}},menubar: {type: String,default: 'file edit insert view format table'},height: {type: Number,required: false,default: 360}},data() {return {hasChange: false,hasInit: false,tinymceId: this.id,fullscreen: false,languageTypeList: {'zh': 'zh_CN'},title: ''}},computed: {language() {return this.languageTypeList[this.$store.getters.language]}},watch: {value(val) {if (!this.hasChange && this.hasInit) {this.$nextTick(() =>window.tinymce.get(this.tinymceId).setContent(val || ''))}},language() {this.destroyTinymce()this.$nextTick(() => this.initTinymce())}},mounted() {// 注: 在此需要傳入一個空對象this.initTinymce({})},activated() {this.initTinymce()},deactivated() {this.destroyTinymce()},destroyed() {this.destroyTinymce()},methods: {initTinymce() {const _this = this// window.tinymce.baseURL = '/tinymces/tinymce'window.tinymce.init({language: this.language,selector: `#${this.tinymceId}`,// 安裝之后,在 node_modules 中找到 tinymce/skins 目錄,然后將 skins 目錄拷貝到 public 的 tinymce 目錄下// 必須加 skin_url 否則會報錯skin_url: '/tinymce/skins/ui/oxide',height: this.height,body_class: 'panel-body ',object_resizing: false,toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,menubar: this.menubar,plugins: plugins,end_container_on_empty_block: true,powerpaste_word_import: 'clean',code_dialog_height: 450,code_dialog_width: 1000,advlist_bullet_styles: 'square',advlist_number_styles: 'default',imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],default_link_target: '_blank',link_title: false,nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugininit_instance_callback: editor => {if (_this.value) {editor.setContent(_this.value)}_this.hasInit = trueeditor.on('NodeChange Change KeyUp SetContent', () => {this.hasChange = truethis.$emit('input', editor.getContent())})},setup(editor) {editor.on('FullscreenStateChanged', (e) => {_this.fullscreen = e.state})}})},destroyTinymce() {const tinymce = window.tinymce.get(this.tinymceId)if (this.fullscreen) {tinymce.execCommand('mceFullScreen')}if (tinymce) {tinymce.destroy()}},setContent(value) {window.tinymce.get(this.tinymceId).setContent(value)},getContent() {window.tinymce.get(this.tinymceId).getContent()},imageSuccessCBK(arr) {console.log(arr)// 處理圖片上傳const _this = thisarr.forEach(v => {window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v}" >`)// tinyMCE.editors[0].setContent(`<img class="wscnph" src="${v}" >`)})},// 點擊 提交submission() {if(this.title.trim() === ''){this.$message({message: '請輸入文章標題',type: 'warning'})return}else if(tinyMCE.activeEditor.getContent().trim() === ''){this.$message({message: '請輸入文章內容',type: 'warning'})return}// console.log(tinyMCE.activeEditor.getContent())console.log(tinyMCE.editors[0].getContent())}} } </script><style scoped lang='scss'> .tinymce-container {position: relative;line-height: normal; } .tinymce-container>>>.mce-fullscreen {z-index: 10000; } .tinymce-textarea {visibility: hidden;z-index: -1; } .editor-custom-btn-container {position: absolute;right: 4px;top: 4px;/*z-index: 2005;*/ } .fullscreen .editor-custom-btn-container {z-index: 10000;position: fixed; } .editor-upload-btn {display: inline-block; }// 標題樣式 .title{& input {margin: 20px 0;height: 36px;line-height: 36px;width: calc(100% - 10px);outline: none;border-radius: 2px;border: 1px solid #D2D2D2;font-size: 16px;padding-left: 10px;}& input:hover {border: 1px solid var(--main-color);} } // 提交,取消按鈕 .publish{margin: 20px 0px; } .publish span{display: inline-block;height: 30px;line-height: 30px;padding: 0 10px;border:1px solid var(--main-color);color: var(--main-color);cursor: pointer;border-radius: 2px;background: #ffffff;margin-right: 20px; } .publish span:hover{background: var(--main-color);color:#ffffff; } .publish .cancel{border:1px solid var(--main-font-color);color: var(--main-font-color); } .publish .cancel:hover{background: var(--main-font-color);color:#ffffff; } </style> // plugins.jsconst plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']export default plugins // toolbar.js// 加粗 斜體 下劃線 刪除線 bold italic underline strikethroug // 居左 居中 居右 兩端對齊 alignleft aligncenter alignright alignjustify // 清除 格式選擇下拉框(縮進、行高) 段落選擇下拉框(段落、標題) 字體選擇下拉框 字號選擇下拉框 alignnone styleselect formatselect fontselect fontsizeselect // 剪切 復制 粘貼 cut copy paste // 減少縮進 增加縮進 outdent indent // 引用 撤銷 恢復 清除格式 blockquote undo redo removeformat // 下標 上標 網格線 插入的集合按鈕 水平線 無序列表 有序列表 subscript superscript visualaid insert hr bullist numlist // 添加和修改鏈接 去除鏈接格式 打開選中鏈接 添加和修改圖片 特殊符號 粘貼純文本 link unlink openlink image charmap pastetext // 打印 預覽 作者 print preview anchor // 分頁符 拼寫檢查 搜索 pagebreak spellchecker searchreplace // 代碼 全屏 插入時間 插入/編輯表格 刪除表格 單元格屬性 合并單元格 拆分單元格 在當前行之前插入一個新行 在當前行之后插入一個新行 刪除當前行 行屬性 剪切選定行 復制選定行 在當前行之前粘貼行 在當前行之后粘貼行 在當前列之前插入一個列 在當前列之后插入一個列 刪除當前列 code fullscreen insertdatetime table tabledelete tablecellprops tablemergecells tablesplitcells tableinsertrowbefore tabledeleterow tablerowprops tablecutrow tablecopyrow tablepasterowbefore tablepasterowafter tableinsertcolbefore tableinsertcolafter tabledeletecol // 在當前行之前插入一個新行 const toolbar = ['bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | alignnone styleselect formatselect fontselect fontsizeselect | cut copy paste | outdent indent | blockquote undo redo removeformat | subscript superscript visualaid insert hr bullist numlist | link unlink openlink image charmap pastetext | print preview anchor | pagebreak spellchecker searchreplace | code fullscreen insertdatetime table tabledelete tablecellprops tablemergecells tablesplitcells tableinsertrowbefore tabledeleterow tablerowprops tablecutrow tablecopyrow tablepasterowbefore tablepasterowafter tableinsertcolbefore tableinsertcolafter tabledeletecol ','hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']export default toolbar四. 實現上傳圖片到七云牛
1.下載
cnpm install qiniu-js var qiniu = require('qiniu-js')// orimport * as qiniu from 'qiniu-js'2. upToken的生成
一般都是后端給的,但是前端也可以實現,我們就在這里以前端的方法實現它
@/utils/quillToken.js
import CryptoJS from 'crypto-js'const utf16to8 = function (str) {/** Interfaces:* utf8 = utf16to8(utf16)* utf16 = utf8to16(utf8)*/var out, i, len, cout = ''len = str.lengthfor (i = 0 ; i < len; i++) {c = str.charCodeAt(i)if ((c >= 0x0001) && (c <= 0x007F)) {out += str.charAt(i)} else if (c > 0x07FF) {out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F))out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F))out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))} else {out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F))out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))}}return out }const base64encode = function (str) {/** Interfaces:* b64 = base64encode(data)* data = base64decode(b64)*/var out, i, lenvar c1, c2, c3len = str.lengthi = 0out = ''var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';while (i < len) {c1 = str.charCodeAt(i++) & 0xffif (i == len) {out += base64EncodeChars.charAt(c1 >> 2)out += base64EncodeChars.charAt((c1 & 0x3) << 4)out += '=='break}c2 = str.charCodeAt(i++)if (i == len) {out += base64EncodeChars.charAt(c1 >> 2)out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4))out += base64EncodeChars.charAt((c2 & 0xF) << 2)out += '='break}c3 = str.charCodeAt(i++)out += base64EncodeChars.charAt(c1 >> 2)out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4))out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6))out += base64EncodeChars.charAt(c3 & 0x3F)}return out }const base64decode = function (str) {var c1, c2, c3, c4var i, len, outlen = str.lengthi = 0out = ''var base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1)while (i < len) {/* c1 */do {c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff]} while (i < len && c1 == -1)if (c1 == -1) break/* c2 */do {c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff]} while (i < len && c2 == -1)if (c2 == -1) breakout += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4))/* c3 */do {c3 = str.charCodeAt(i++) & 0xffif (c3 == 61) return outc3 = base64DecodeChars[c3]} while (i < len && c3 == -1)if (c3 == -1) breakout += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2))/* c4 */do {c4 = str.charCodeAt(i++) & 0xffif (c4 == 61) return outc4 = base64DecodeChars[c4]} while (i < len && c4 == -1)if (c4 == -1) breakout += String.fromCharCode(((c3 & 0x03) << 6) | c4)}return out }const safe64 = function (base64) {base64 = base64.replace(/\+/g, '-')base64 = base64.replace(/\//g, '_')return base64 }const genUpToken = function () {// 參數 accessKey,secretKey,putPolicyvar accessKey = 'q5Oqby268SGSsWEBrkwGW9oQ20qzi2-fXl6Xm1zL'var secretKey = 'B7IQmIhh38gIHXEDccW8YN4Yath8vHpwf_aifeDW'var putPolicy = {'scope':'lyajuan','deadline':Math.round(new Date().getTime() / 1000) + 3600}// SETP 2var putPolicy1 = JSON.stringify(putPolicy)// SETP 3var encoded = base64encode(utf16to8(putPolicy1))// SETP 4var hash = CryptoJS.HmacSHA1(encoded, secretKey)var encodedSigned = hash.toString(CryptoJS.enc.Base64)// SETP 5var uploadToken = accessKey + ':' + safe64(encodedSigned) + ':' + encodedreturn uploadToken } export {utf16to8,base64encode,base64decode,safe64 ,genUpToken }在需要生成 Token 的 .vue 文件中引入
import {genUpToken} from '@/utils/qiniuToken.js'安裝:crypto-js 加密: https://www.jianshu.com/p/a47477e8126a
cnpm install crypto-js --save <template><div class="chooseImage" @click="choose"><svg-icon icon-class='upload'/>選擇圖片<input type="file" class="pickFile" @change="uploadFile" ref='chooseFile' title="上傳文件" multiple :style="{background:color,borderColor:color}"/></div> </template><script> // 引入七云牛js文件 import * as qiniu from 'qiniu-js' // 生成token的文件 import {genUpToken} from '@/utils/qiniuToken.js' import store from '@/store'export default {name: 'chooseImage',props: {color: {type: String,default: 'var(--main-color)'}},data() {return {fileList: []}},methods: {choose() {this.$refs.chooseFile.click()},// 上傳文件uploadFile($event) {store.dispatch('SetLoading',true)const file = $event.target.filesfor(var i=0;i<file.length; i++) {// 限制上傳文件的大小為200M// console.log(file[i])if (file[i].size > 209715200) {const cur_size = Math.floor(file.size * 100 / 1024 / 1024) / 100this.$notify.info({title: '消息',message: '上傳文件大小不得超過200M 當前文件' + cur_size + 'M '})return false}// this.showProgress = trueconst token = genUpToken();const fileName = file[i].nameconst suffix = fileName.substring(fileName.lastIndexOf('.')) // 后綴名const prefix = fileName.substring(0, fileName.lastIndexOf('.'))const key = prefix + token + suffix // 上傳文件名const observer = {next: response => {// 上傳進度'+Math.floor(response.total.percent)+'%'// total.loaded: number,已上傳大小,單位為字節。// total.total: number,本次上傳的總量控制信息,單位為字節,注意這里的 total 跟文件大小并不一致。// total.percent: number,當前上傳進度,范圍:0~100。this.uploadProgress = Math.floor(response.total.percent)if(this.uploadProgress == 100){this.$message('上傳成功!')}},error: err => {// 上傳失敗觸發this.$message.error('上傳失敗' + err.message)console.log(err)},complete: response => {this.uploadProgress = 0this.showProgress = falsethis.fileList.push('http://poxcqlozi.bkt.clouddn.com/' + response.key)}}// 可通過 subscription.unsubscribe() 停止當前文件上傳 const putExtra = {// 文件原文件名fname: '',// 用來放置自定義變量params: {},// 用來限制上傳文件類型,為 null 時表示不對文件類型限制// 限制類型放到數組里,如 mimeType: mimeType: ['image/png', 'image/jpeg', 'image/gif']}const config = {// 是否使用 cdn 加速域名,默認falseuseCdnDomain: true,// 上傳域名區域,當為 null 或 undefined 時,自動分析上傳域名區域region: qiniu.region.z1}/*file: Blob 對象,上傳的文件key: 文件資源名token: 上傳驗證信息,前端通過接口請求后端獲得config: object*/// 關鍵代碼let options = {quality: 0.92,noCompressIfLarger: true,maxWidth: 800,maxHeight: 618}qiniu.compressImage(file[i], options).then(data => {// data : {// dist: 壓縮后輸出的 blob 對象,或原始的 file,具體看下面的 options 配置// width: 壓縮后的圖片寬度// height: 壓縮后的圖片高度// }var observable = qiniu.upload(data.dist, key, token, putExtra, config)var subscription = observable.subscribe(observer) // 上傳開始});}this.$emit('successCBK', this.fileList)this.fileList = []}} } </script><style lang="scss" scoped> .chooseImage{text-align: center;background: var(--main-color);color: #ffffff;border-radius: 5px;padding: 5px 10px;& input{display: none;}& .svg-icon{margin-right: 10px;} } </style>?
總結
以上是生活随笔為你收集整理的Vue 中使用 Tinymce 富文本编辑器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树莓派--搭建nextcloud私有云
- 下一篇: PDF的文件大小怎么压缩,两款高效的PD