Vue background management system rich text component (1) tinymce
Introduction
As one of the most important basic components of the background management system, rich text components must be selected from rich text manufacturers with fewer pits. The tinymce used here is nice and less pitted.
Mainly rely on instructions (install first, steps omitted)
{
"axios": "^0.18.0",
"element-ui": "2.11.1",
"vue": "^2.6.10",
"vue-router": "^3.0.1"
}
tinymce:5.0.8 The method of downloading the source code is directly used here, npm download greatly affects the packaging speed, and you can also use cdn
Tinymce official website download address
text
1. Download the tinymce source code and place it in the static folder under the public folder generated by vue-cli 3, as shown in the figure
If you need a language pack, download the zh_CN.js file separately and place it in the lang folder under the tinymce_5.0.8 folder
zh_CN.js official website download address
2. Import in the entry html file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<meta name="description" content="<%= webpackConfig.name %>" />
<meta name="robots" content="noindex,nofollow,noarchive" />
<title>title</title>
</head>
<body>
<script src="<%= BASE_URL %>static/tinymce_5.0.8/tinymce.min.js"></script>
<div id="app"></div>
</body>
</html>
3. Components Tinymce
File Directory
src/components/Tinymce/index.vue
<template>
<div class="tinymce-container editor-container" :class="{fullscreen:fullscreen}">
<textarea :id="tinymceId" class="tinymce-textarea" />
<div class="editor-custom-btn-container">
<multiple-upload class="editor-upload-btn" @success="imageSuccess" />
</div>
</div>
</template>
<script>
import axios from 'axios'
// MultipleUpload组件见 https://blog.csdn.net/qq_39953537/article/details/100039094
import MultipleUpload from '@/components/MultipleUpload'
import plugins from './plugins' // 见下文
import toolbar from './toolbar' // 见下文
// 上传html片段接口根据自己项目更换
import {
uploadHtml } from '@/api/upload'
export default {
name: 'Tinymce',
components: {
MultipleUpload
},
props: {
// 默认填充到富文本的html文件
html: {
type: String,
default: ''
},
toolbar: {
type: Array,
default() {
return []
}
},
menubar: {
type: Boolean,
default: false
},
height: {
type: Number,
default: 400
}
},
data() {
return {
hasChange: false,
hasInit: false,
tinymceId: 'vue-tinymce-' + +new Date(),
fullscreen: false,
value: '',
editorContent: ''
}
},
watch: {
value(val) {
this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || '')
)
},
html(val) {
if (this.isUrl) {
this.loadUrl(val)
}
}
},
created() {
if (this.html && this.html.startsWith('http')) {
this.loadUrl(this.html)
} else {
this.value = this.html + ''
this.editorContent = this.html + ''
}
},
mounted() {
this.initTinymce()
},
activated() {
this.initTinymce()
},
deactivated() {
this.destroyTinymce()
},
destroyed() {
this.destroyTinymce()
},
methods: {
initTinymce() {
window.tinymce.init({
fontsize_formats: '12px 14px 16px 18px 20px 24px 36px',
language: 'zh_CN',
language_url: '/static/tinymce_5.0.8/langs/zh_CN.js',
selector: `#${
this.tinymceId}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: true,
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',
default_link_target: '_blank',
link_title: false,
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())
this.editorContent = editor.getContent()
})
},
setup(editor) {
editor.on('FullscreenStateChanged', e => {
this.fullscreen = e.state
})
}
})
},
destroyTinymce() {
if (window.tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy()
}
},
loadUrl(url) {
if (url && url.length > 0) {
axios
.get(url)
.then(response => {
// 处理HTML显示
this.value = response.data
this.editorContent = response.data
this.$emit('subLoadUrlToHtml', response.data)
this.$emit('input', response.data)
})
.catch(() => {
this.value = '服务器数据加载失败,请重试!'
})
}
},
// 设置编辑器内容
setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value)
},
// 获取编辑器内容
getContent() {
window.tinymce.get(this.tinymceId).getContent()
},
// 图片上传成功后填充到富文本编辑器
async imageSuccess(urlList) {
try {
let imageTemplateList = ''
urlList.forEach(item => {
const image = `<img style="max-width:100%;" src="${
item}">`
imageTemplateList = imageTemplateList + image
})
window.tinymce.get(this.tinymceId).insertContent(imageTemplateList)
this.$message({
message: '上传成功!',
type: 'success'
})
} catch (error) {
console.log(error)
this.$message({
message: error,
type: 'error'
})
}
},
// 编辑器内容上传到cos,调用返回url
async content2Url() {
try {
const res = await uploadHtml(this.editorContent)
return res
} catch (error) {
this.$message({
message: error.data.message,
type: 'error'
})
}
}
}
}
</script>
<style lang='scss' >
#tinymce {
background-color: blue;
p {
margin: 0;
}
}
.tinymce-container {
position: relative;
}
.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;
}
// 隐藏底部logo栏
.mce-edit-area + .mce-statusbar {
opacity: 0;
height: 0;
}
</style>
src/components/Tinymce/plugins.js
const plugins = [
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount paste'
]
export default plugins
src/components/Tinymce/toolbar.js
const toolbar = ['formatselect fontsizeselect forecolor backcolor bold italic underline strikethrough alignleft aligncenter alignright outdent indent removeformat hr undo redo']
export default toolbar
4. Use
<template>
<div>
<tinymce
ref="tinymce"
:height="500"
:html="html"
@input="getContent"
/>
</div>
</template>
<script>
import AppCropper from '@/components/Cropper'
export default {
name: 'GoodsForm',
components: {
AppCropper
},
data() {
return {
html: 'https://ebusiness-1255313385.cosbj.myqcloud.com/image/20190823/center2019082304054532.html',
content:''
}
},
methods: {
// 获取编辑器内容
getContent(content) {
this.content = content
},
// 编辑器内容转换成在线url
async getcontent2Url() {
try {
const htmlUrl = await this.$refs.tinymce.content2Url()
return htmlUrl
} catch (error) {
console.log(error)
}
}
}
}
</script>
5. Use effect
Reference link
1.https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/Tinymce/index.vue