Vue项目中使用Tinymce,图片上传服务器接口对接方法
微wx笑 2021-01-21【前端开发】 4 0关键字: Vue Tinymce 图片上传 接口对接
TinyMCE提供了图片上传处理函数images_upload_handler, 该函数有三个参数:blobInfo,success callback,failure callback, 分别是图片内容, 一个成功的回调函数以及一个失败
TinyMCE提供了图片上传处理函数images_upload_handler, 该函数有三个参数:blobInfo,success callback,failure callback, 分别是图片内容, 一个成功的回调函数以及一个失败的回调函数,具体上传图片代码在上面已经写,这里就不赘述; 需要注意的是,当向后台上传完图片, 我们要调用success函数来用服务器地址替换<image>标签的src属性。
以下是我基于“vue-element-admin”的项目图片上传服务器接口对接方法
src/components/Tinymce/index.vue
<template> <div :class="{fullscreen:fullscreen}" :style="{width:containerWidth}"> <textarea :id="tinymceId" /> <!-- <div> <editorImage color="#1890ff" @successCBK="imageSuccessCBK" /> </div> --> </div> </template> <script> /** * docs: * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce */ // import editorImage from './components/EditorImage' import plugins from './plugins' import toolbar from './toolbar' import load from './dynamicLoadScript' import { uploadFile } from '@/api/upload.js' // why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js' export default { name: 'Tinymce', // 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, String], required: false, default: 360 }, width: { type: [Number, String], required: false, default: 'auto' } }, data() { return { hasChange: false, hasInit: false, tinymceId: this.id, fullscreen: false, languageTypeList: { 'en': 'en', 'zh': 'zh_CN', 'es': 'es_MX', 'ja': 'ja' } } }, computed: { language() { return this.languageTypeList[this.$store.getters.language] }, containerWidth() { const width = this.width if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` return `${width}px` } return width } }, 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.init() }, activated() { if (window.tinymce) { this.initTinymce() } }, deactivated() { this.destroyTinymce() }, destroyed() { this.destroyTinymce() }, methods: { init() { // dynamic load tinymce from cdn load(tinymceCDN, (err) => { if (err) { this.$message.error(err.message) return } this.initTinymce() }) }, 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, 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: true, 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()) }) }, setup(editor) { editor.on('FullscreenStateChanged', (e) => { _this.fullscreen = e.state }) }, automatic_uploads: true, images_upload_handler: (blobInfo, success, failure) => { const formData = new FormData() formData.append('file', blobInfo.blob()) uploadFile(formData).then(res => { if (res.data) { console.log('res.data', res.data) success(res.data) return } failure('上传失败') }).catch(() => { failure('上传出错') }) }, // it will try to keep these URLs intact // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/ // https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions convert_urls: false // 整合七牛上传 // 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() { 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) { arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img src="${v.url}" >`)) } } } </script> <style scoped> .tinymce-container { position: relative; line-height: normal; } .tinymce-container { ::v-deep { .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>
这里面主要的几个部分改动
1、支持粘贴图片
写文章插入图片的时候,经常会用到屏幕截图,截图后直接粘贴到编辑器中,是比较方便的。但是默认这个功能并没有开启,需要添加以下配置:
paste_data_images: true,
2、支持自动上传
automatic_uploads: true,
3、编写上传处理程序
images_upload_handler: (blobInfo, success, failure) => { const formData = new FormData() formData.append('file', blobInfo.blob()) uploadFile(formData).then(res => { if (res.data) { console.log('res.data', res.data) success(res.data) return } failure('上传失败') }).catch(() => { failure('上传出错') }) },
这里的 res.data 就是上传到服务器之后的图片地址,我对接的后端接口 response 的内容是JSON结构,类似下面这样:
{ "code": 200, "message": "成功", "data": "/ufs/2021/1/17c5a6fd70804ff784ed21b112854b60.png" }
4、上传图片方法 uploadFile
import { uploadFile } from '@/api/upload.js'
src/api/upload.js
import request from '@/utils/request' export function uploadFile(data) { return request({ url: '/api/common/upload', method: 'post', headers: { 'Content-Type': 'multipart/form-data' }, data }) }
这里比较重要的是 headers 'Content-Type': 'multipart/form-data',只有设置为'multipart/form-data'才能上传文件。
本文由 微wx笑 创作,采用 署名-非商业性使用-相同方式共享 4.0 许可协议,转载请附上原文出处链接及本声明。
原文链接:https://www.ivu4e.cn/blog/front/2021-01-21/612.html