1.这是一个富文本组件
<template>
<div :class="{ fullscreen: fullscreen }" class="tinymce-container" :style="{ width: containerWidth }">
<textarea :id="tinymceId" class="tinymce-textarea" />
</div>
</template>
<script>
// import { api } from '@/api/api'
// 这里是请求接口的地方,当你需要上传图片的时候需要在这里配置请求-具体看代码改
// import { httpAction } from '@/api/manage'
const tinymceCDN = 'https://lib.baomitu.com/tinymce/5.1.2/tinymce.min.js'
// const tinymceCDN = 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.1.2/tinymce.min.js';
const plugins = [
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks wordcount'
]
const plugins2 = [
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr insertdatetime link lists nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks wordcount'
]
const toolbar = [
'fontsizeselect formatselect searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'
]
function loadedTinymce() {
return window.tinymce
}
export default {
name: 'Tinymce',
props: {
id: {
type: String,
default: function() {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
}
},
value: {
type: String,
default: ''
},
toolbar: {
type: Array,
required: false,
default() {
return []
}
},
menubar: {
type: String,
default: 'file edit insert view format table'
},
height: {
type: [Number, String],
required: false,
default: 360
},
width: {
type: [Number, String],
required: false,
default: 'auto'
},
imgPass: {
type: Boolean,
required: false,
default: false
}
},
data() {
return {
// 这里是项目的后台接口地址,自己配置
// requestUrl: process.env.VUE_APP_API_BASE_URL,
imgFlag: false,
imgArr: [],
hasChange: false,
hasInit: false,
tinymceId: this.id,
fullscreen: false,
languageTypeList: {
en: 'en',
zh: 'zh_CN',
es: 'es_MX',
ja: 'ja'
},
// 这里是图片的上传接口地址,复制自己项目图片上传接口地址扔到这里
imgApi: 'https://jsonplaceholder.typicode.com/posts/'
}
},
computed: {
language() {
return this.languageTypeList['zh']
},
containerWidth() {
const width = this.width
if (/^[\d]+(\.[\d]+)?$/.test(width)) {
return `${width}px`
}
return width
}
},
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val || ''))
}
// str是获取的富文本解析成html格式的文本内容
const str = window.tinymce.activeEditor.getContent()
// num是用正则获取到的纯文本内容(包括标点符号)
const num = this.removeHTMLTag(str)
this.$emit('textLength', num.length)
if (num.length > 1000) {
// this.Inputflag
// this.$confirm('字符长度已超出上限1000个字符!', '提示', {
// confirmButtonText: '确定',
// cancelButtonText: '取消',
// type: 'warning'
// });
}
},
imgPass: {
immediate: true,
handler(newVal, oldValue) {
this.imgFlag = newVal
}
},
language() {
this.destroyTinymce()
this.$nextTick(() => this.initTinymce())
}
},
mounted() {
this.init()
},
activated() {
if (window.tinymce) {
this.initTinymce()
}
},
deactivated() {
this.destroyTinymce()
},
destroyed() {
this.destroyTinymce()
},
methods: {
// 提纯文本的方法
removeHTMLTag: function(str) {
str = str.replace(/<\/?[^>]*>/g, '')
str = str.replace(/[ | ]*\n/g, '\n')
str = str.replace(/ /gi, '')
str = str.replace(/\s/g, '')
return str
},
init() {
// dynamic load tinymce from cdn
this.dynamicLoadScript(tinymceCDN, (err) => {
if (err) {
this.$message.error(err.message)
return
}
this.initTinymce()
})
},
initTinymce() {
const _this = this
// 具体配置信息可百度- -
window.tinymce.init({
// 配置 中文包 地址:这个包是扔在服务器上面的,然后直接请求服务器上面那个包地址就行了
// language_url: 'http://192.168.0.xxx:xxxx/xxx/zh_CN.js',
language: this.language,
selector: `#${this.tinymceId}`,
images_upload_url: _this.imgApi,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar,
plugins: this.imgFlag ? plugins2 : 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: [
process.env.VUE_APP_API_BASE_URL,
// 'hhttp://192.168.0.xxx',
'www.tinymce.com'
],
default_link_target: '_blank',
link_title: false,
branding: true,
statusbar: false,
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
autosave_ask_before_unload: true,
autosave_interval: '30s',
autosave_prefix: '{path}{query}-{id}-',
autosave_restore_when_empty: false,
autosave_retention: '2m',
init_instance_callback: (editor) => {
if (_this.value) {
editor.setContent(_this.value)
}
_this.hasInit = true
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true
this.$emit('input', editor.getContent())
})
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
},
// 图片上传 配置地方
images_upload_handler(blobInfo, success, failure, progress) {
progress(0)
// 创建文件流
const formData = new FormData()
// 设置文件流参数
formData.append('fileType', '0')
formData.append('file', blobInfo.blob(), _this.imgApi)
/* httpAction(_this.imgApi, formData, 'post').then((res) => {
// 这里配置请求图片的地址是 'https://192.168.0.xxx:9999/file/?id=2323213123123123'
// 根据实际项目需求自己配置 主要是 formData 这个文件流扔给后台
success(_this.requestUrl + _this.imgApi.get + '?id=' + res.files[0].id)
progress(100)
}) */
}
})
},
destroyTinymce() {
const tinymce = window.tinymce.get(this.tinymceId)
if (this.fullscreen) {
tinymce.execCommand('mceFullScreen')
}
if (tinymce) {
tinymce.destroy()
}
},
setContent(value) {
this.$nextTick(() => {
window.tinymce.get(this.tinymceId).setContent(value)
})
},
getContent() {
window.tinymce.get(this.tinymceId).getContent()
},
imageSuccessCBK(arr) {
const _this = this
arr.forEach((v) => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
})
},
dynamicLoadScript(src, callback) {
let callbacks = []
const existingScript = document.getElementById(src)
const cb = callback || function() {}
if (!existingScript) {
const script = document.createElement('script')
script.src = src
script.id = src
document.body.appendChild(script)
callbacks.push(cb)
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
onEnd(script)
}
if (existingScript && cb) {
if (loadedTinymce()) {
cb(null, existingScript)
} else {
callbacks.push(cb)
}
}
function stdOnEnd(script) {
script.onload = function() {
// this.onload = null here is necessary
// because even IE9 works not like others
this.onerror = this.onload = null
for (const cb of callbacks) {
cb(null, script)
}
callbacks = null
}
script.onerror = function() {
this.onerror = this.onload = null
cb(new Error('Failed to load ' + src), script)
}
}
function ieOnEnd(script) {
script.onreadystatechange = function() {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
for (const cb of callbacks) {
cb(null, script) // there is no way to catch loading errors in IE8
}
callbacks = null
}
}
}
}
}
</script>
<style scoped>
.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;
}
</style>
2 这里是引用
<template>
<div>
<div
style="max-width: 1000px; min-width: 500px; padding: 3px 1px"
>
<tinymce :id="tinymceId" v-model="tinymce" :height="426" />
</div>
</div>
</template>
<script>
import tinymce from '@/components/tinymce'
export default {
components: {
tinymce
},
data() {
return {
tinymceId: '1',
tinymce: ''
}
}
}
</script>
<style lang="scss" scoped>
</style>
3 结果图
1.之所以是英文的是因为没有设置服务器中文包地址,代码里面有介绍
2.配置图片上传这个得自己加上自己项目里面的图片上传地址 和图片回显地址,直接 搜索 image, requestUrl。贼简单
3.如果出现多个富文本的时候 请用v-if去显示隐藏它,必须要让它销毁了才会创建新的,有的弹框需要先创建弹框再创建富文本,也就是需要两个v-if,里面的id可以去看看
4.这个是古老写法,cdn加速那种,tinymce是支持npm去下载包的写法的,具体可以去研究一下,配置项基本大差不差