项目中初次用tinymce的时候遇到一大推问题,感觉这个组件太庞大,太笨重,尤其是网站反应那个慢啊,转半天反应不过来,api多的类找都找不到,现在都觉得很难驾驭它,下面是项目中所用到的api的整理:
项目目录:
index.vue 对原来的Tinymce组件做了稍许修改
<textarea :id="tinymceId" class="tinymce-textarea"/>
<div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/>
</div>
import editorImage from './components/editorImage'
import plugins from './plugins'
import toolbar from './toolbar'
import emitter from 'element-ui/src/mixins/emitter'
export default {
name: 'Tinymce',
mixins: [emitter],
components: { editorImage },
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,
required: false,
default: 50
},
pasteImages: {
type: Boolean,
default: false
}
},
data () {
return {
hasChange: false,
hasInit: false,
tinymceId: this.id,
fullscreen: false,
languageTypeList: {
'en': 'en',
'zh': 'zh_CN'
}
}
},
computed: {
language () {
return this.languageTypeList[this.$store.getters.language]
}
},
watch: {
value (val) {
try {
if (this.value !== this.getContent()) {
this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || ''))
}
} catch (e) {
}
},
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.init({
language: this.language,
selector: `#${this.tinymceId}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar,
plugins: plugins,
contextmenu: 'link',
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,
paste_data_images: this.pasteImages,
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
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())
})
editor.on('blur', () => {
_this.dispatch('ElFormItem', 'el.form.blur', [editor.getContent()])
})
},
setup (editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
editor.on('init', function () {
if (editor.getContent() === '') {
editor.setContent('<p id=\'#imThePlaceholder\'>请输入内容(限2000个字符)!</p>')
}
})
editor.on('focus', function () {
editor.setContent(_this.value)
})
}
// 整合七牛上传
// images_dataimg_filter(img) {
// setTimeout(() => {
// const $image = $(img);
// $image.removeAttr('width');
// $image.removeAttr('height');
// if ($image[0].height && $image[0].width) {
// $image.attr('data-wscntype', 'image');
// $image.attr('data-wscnh', $image[0].height);
// $image.attr('data-wscnw', $image[0].width);
// $image.addClass('wscnph');
// }
// }, 0);
// return img
// },
// images_upload_handler(blobInfo, success, failure, progress) {
// progress(0);
// const token = _this.$store.getters.token;
// getToken(token).then(response => {
// const url = response.data.qiniu_url;
// const formData = new FormData();
// formData.append('token', response.data.qiniu_token);
// formData.append('key', response.data.qiniu_key);
// formData.append('file', blobInfo.blob(), url);
// upload(formData).then(() => {
// success(url);
// progress(100);
// })
// }).catch(err => {
// failure('出现未知问题,刷新页面,或者联系程序员')
// console.log(err);
// });
// },
})
},
destroyTinymce () {
try {
if (window.tinymce.get(this.tinymceId) && window.tinymce.get(this.tinymceId) != null) {
window.tinymce.get(this.tinymceId).destroy()
}
} catch (e) {}
},
setContent (value) {
window.tinymce.get(this.tinymceId).setContent(value)
},
getContent () {
return 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}" >`)
})
}
}
}
plugins.js 和 toolbar.js里面放的是插件,可以对里面的插件增加和删除不必要的插件;editorImage.vue是上传本地图片插件,但是不能上传黏贴过来的图片
editorImage.vue
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary"
@click=" dialogVisible=true">上传图片
</el-button>
<el-dialog :visible.sync="dialogVisible" append-to-body>
<el-upload
:multiple="true"
:file-list="fileList"
:show-file-list="true"
:on-remove="handleRemove"
:on-success="handleSuccess"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
class="editor-slide-upload"
action="https://httpbin.org/post"
list-type="picture-card">
<el-button size="mini" type="primary">点击上传</el-button>
</el-upload>
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="handleSubmit">确 定</el-button>
</el-dialog>
// import { getToken } from 'api/qiniu'
export default {
name: 'EditorSlideUpload',
props: {
color: {
type: String,
default: '#1890ff'
}
},
data () {
return {
dialogVisible: false,
listObj: {},
fileList: []
}
},
methods: {
checkAllSuccess () {
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
},
handleSubmit () {
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
if (!this.checkAllSuccess()) {
this.$message('请耐心等待所有图片上传成功!')
return
}
this.$emit('successCBK', arr)
this.listObj = {}
this.fileList = []
this.dialogVisible = false
},
handleSuccess (response, file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
this.listObj[objKeyArr[i]].url = response.files.file
this.listObj[objKeyArr[i]].hasSuccess = true
return
}
}
},
handleRemove (file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
delete this.listObj[objKeyArr[i]]
return
}
}
},
/**上传文件之前的钩子(参数为上传的文件)**/
beforeUpload (file) {
const _URL = window.URL || window.webkitURL
const fileName = file.uid
this.listObj[fileName] = {}
//图片类型和大小判断
let reg = new RegExp('[.jpg|.png]$')
if (Math.floor(file.size / 1024 / 1024) < 2) {
if (file && reg.test(file.name)) {
} else {
this.$message.error('图片格式不正确,请上传JPG或PNG格式的图片!')
return false
}
} else {
this.$message.error('只能上传小于2MB的图片!')
return false
}
return new Promise((resolve, reject) => {
const img = new Image()
img.src = _URL.createObjectURL(file)
if (img) {
img.onload = () => {
this.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
}
}
resolve(true)
})
},
handleExceed (files, fileList) {}//文件超出个数限制时的钩子
}
}
项目中遇到的问题:
1、无法黏贴截图过来的图片
2、无法在富文本内部添加placeholder占位符
第一个问题:
解决第一个问题主要用到的是tinymce组件的paste_data_images属性,Boolean类型,为true时允许黏贴图片,false不允许黏贴图片
第二个问题:
解决第二个问题用到的就是setup方法,再利用编辑器事件的init事件给文本框设置占位符,就是在富文本编辑器初始渲染的时候添加placeholder,即:
editor.on('init', function () {
if (editor.getContent() === '') {
editor.setContent('<p id=\'#imThePlaceholder\'>请输入内容(限2000个字符)!</p>')
}
})
再利用当focus事件时清除初始化时的placeholder的内容
editor.on('focus', function () {
editor.setContent(_this.value)
})