富文本编辑器TinyMCE的使用(React Vue)

富文本编辑器TinyMCE的使用(React Vue)

一,需求与介绍

1.1,需求

编辑新闻等富有个性化的文本

1.2,介绍

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。

TinyMCE的优势:

  • 开源可商用,基于LGPL2.1
  • 插件丰富,自带插件基本涵盖日常所需功能
  • 接口丰富,可扩展性强,有能力可以无限拓展功能
  • 界面好看,符合现代审美
  • 提供经典、内联、沉浸无干扰三种模式
  • 对标准支持优秀(自v5开始)
  • 多语言支持,官网可下载几十种语言。

二,配置集成并组件化

2.1,通用配置

1,工具栏toolbar

1 // Here is a list of the toolbar
2 // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
3
4 // const toolbar = [‘searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample‘, ‘hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor‘]// fullscreen
5 const toolbar = [‘code codesample undo redo restoredraft | cut copy paste pastetext | forecolor backcolor searchreplace bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | formatselect fontselect fontsizeselect | blockquote subscript superscript removeformat | table image media charmap emoticons hr pagebreak insertdatetime print preview‘]// | fullscreen
6 export default toolbar

2,插件plugins

1 // Any plugins you want to use has to be imported
2 // Detail plugins list see https://www.tinymce.com/docs/plugins/
3 // Custom builds see https://www.tinymce.com/download/custom-builds/
4
5 // 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 visualchars wordcount‘]
6 const plugins = [‘print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount imagetools textpattern help paste emoticons autosave‘]
7
8 export default plugins

3,常用字体配置fonts

1 // Any font you want to use has to be imported
2 const fontsizeFormats=‘12px 14px 16px 18px 24px 36px 48px 56px 72px‘;
3 const fontFormats= ‘微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;知乎配置=BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif;小米配置=Helvetica Neue,Helvetica,Arial,Microsoft Yahei,Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif‘;
4
5 export default {
6     fontsizeFormats,
7     fontFormats
8 }

4,准备标签

1   <div>
2               <textarea id="tinymceId"/>
3             </div>

在textarea外面需要套一层div,否则会产生一些意想不到的问题

5,初始化标签,生成编辑框

 1  window.tinymce.init({
 2       language: ‘zh_CN‘,
 3       selector: `#${tinymceId}`,
 4       height: height,
 5       body_class: ‘panel-body ‘,
 6       object_resizing: false,
 7       toolbar: toolbar.length > 0 ? toolbar : defaultToolbar,
 8       menubar: menubar,
 9       plugins: defaultplugins,
10       end_container_on_empty_block: true,
11       fontsize_formats: fontsizeFormats,
12       font_formats: fontFormats,
13       powerpaste_word_import: ‘clean‘,
14       code_dialog_height: 450,
15       code_dialog_width: 1000,
16       advlist_bullet_styles: ‘square‘,
17       advlist_number_styles: ‘default‘,
18       imagetools_cors_hosts: [‘www.tinymce.com‘, ‘codepen.io‘],
19       default_link_target: ‘_blank‘,
20       link_title: false,
21       nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
22       init_instance_callback: editor => {
23         if (content) {
24           editor.setContent(content)
25         }
26         editor.on(‘NodeChange Change KeyUp SetContent‘, () => {
27         })
28       },
29       setup(editor) {
30         editor.on(‘FullscreenStateChanged‘, (e) => {
31         })
32       }
33     })

2.2,React组件化

直接放代码

  1 import React from ‘react‘;
  2 import { Button } from ‘antd‘;
  3 import PropTypes from ‘prop-types‘;
  4 import Page from ‘../../components/Pages/page‘;
  5 import styles from ‘./index.less‘;
  6 import defaultplugins from ‘./plugins‘;
  7 import defaultToolbar from ‘./toolbar‘;
  8 import {
  9   fontsizeFormats,
 10   fontFormats
 11 } from ‘./font‘;
 12 import UploadImage from ‘./UploadImage‘;
 13
 14
 15 class Tinymce extends React.Component {
 16
 17   static propTypes = {
 18     tinymceId: PropTypes.string,
 19     content: PropTypes.string,
 20     toolbar: PropTypes.array,
 21     menubar: PropTypes.string,
 22     height: PropTypes.number,
 23     getContent: PropTypes.func,
 24   };
 25   static defaultProps = {
 26     tinymceId: ‘react-tinymce-‘ + +new Date() + ((Math.random() * 1000).toFixed(0) + ‘‘),
 27     menubar: ‘file edit insert view format table‘,
 28     height: 520,
 29     toolbar: []
 30   };
 31   constructor(props) {
 32     super(props);
 33     this.state = {
 34       hasChange: false,
 35       hasInit: false,
 36       fullscreen: false,
 37     };
 38   };
 39
 40   componentDidMount() {
 41     this.initTinymce()
 42
 43   }
 44   componentWillUnmount() {
 45     this.destroyTinymce()
 46   }
 47   initTinymce() {
 48     const { tinymceId, menubar, height, toolbar, content, getContent } = this.props
 49     const _this = this
 50     window.tinymce.init({
 51       language: ‘zh_CN‘,
 52       selector: `#${tinymceId}`,
 53       height: height,
 54       body_class: ‘panel-body ‘,
 55       object_resizing: false,
 56       toolbar: toolbar.length > 0 ? toolbar : defaultToolbar,
 57       menubar: menubar,
 58       plugins: defaultplugins,
 59       end_container_on_empty_block: true,
 60       fontsize_formats: fontsizeFormats,
 61       font_formats: fontFormats,
 62       powerpaste_word_import: ‘clean‘,
 63       code_dialog_height: 450,
 64       code_dialog_width: 1000,
 65       advlist_bullet_styles: ‘square‘,
 66       advlist_number_styles: ‘default‘,
 67       imagetools_cors_hosts: [‘www.tinymce.com‘, ‘codepen.io‘],
 68       default_link_target: ‘_blank‘,
 69       link_title: false,
 70       nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
 71       init_instance_callback: editor => {
 72         if (content) {
 73           editor.setContent(content)
 74         }
 75         _this.setState({
 76           hasInit: true
 77         })
 78         editor.on(‘NodeChange Change KeyUp SetContent‘, () => {
 79           _this.setState({
 80             hasChange: true
 81           })
 82         })
 83       },
 84       setup(editor) {
 85         editor.on(‘FullscreenStateChanged‘, (e) => {
 86           _this.setState({
 87             fullscreen: e.state
 88           })
 89         })
 90       }
 91     })
 92   }
 93   destroyTinymce() {
 94     const { tinymceId } = this.props
 95     const { fullscreen } = this.state
 96     const tinymce = window.tinymce.get(tinymceId)
 97     if (fullscreen) {
 98       tinymce.execCommand(‘mceFullScreen‘)
 99     }
100
101     if (tinymce) {
102       tinymce.destroy()
103     }
104   }
105   // setContent(value) {
106   //   const { tinymceId } = this.props
107   //   window.tinymce.get(tinymceId).setContent(value)
108   // }
109   saveToGetContent() {
110     const { tinymceId, getContent } = this.props
111     if (getContent && typeof getContent === ‘function‘) {
112       getContent(window.tinymce.get(tinymceId).getContent())
113     }
114   }
115
116   /**
117    * 上传图片成功回调
118    *  */
119   imageSuccessCBK(arr) {
120     const { tinymceId } = this.props
121     arr.forEach(v => {
122       window.tinymce.get(tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
123     })
124   }
125   render() {
126     const { loading, tinymceId } = this.props
127     const { fullscreen } = this.state
128     const header = (
129       <Page.Header breadcrumb={[‘富文本实例‘]} title={‘富文本实例‘} />
130     );
131     return (
132       <div className={styles[‘tinymce-components‘]}>
133         <Page header={header} loading={!!loading}>
134
135           <div className={fullscreen ? "tinymce-container mce-fullscreen" : "tinymce-container"}>
136
137             <div>
138               <textarea id={tinymceId} className={styles[‘tinymce-textarea‘]} />
139             </div>
140             <div className="editor-custom-btn-container">
141               <UploadImage className="editor-upload-btn" imageSuccessCBK={(arr)=>{this.imageSuccessCBK(arr)}}/>
142             </div>
143             <Button type="primary" onClick={() => { this.saveToGetContent() }}>保存</Button>
144           </div>
145         </Page>
146       </div>
147     );
148   }
149 }
150
151 export default Tinymce;

上传图片组件,使用antd的部分组件:

  1 import React from ‘react‘;
  2 import { Button, Modal, Icon, Upload, message } from ‘antd‘;
  3 import PropTypes from ‘prop-types‘;
  4 import styles from ‘./index.less‘;
  5
  6 const Dragger = Upload.Dragger;
  7
  8 class UploadImage extends React.Component {
  9
 10   static propTypes = {
 11     imageSuccessCBK: PropTypes.func,
 12
 13   };
 14   static defaultProps = {
 15
 16   };
 17   constructor(props) {
 18     super(props);
 19     this.state = {
 20       visible: false,
 21       listObj: {}
 22     };
 23   };
 24
 25   /**
 26    * 显示弹框
 27    *
 28    *  */
 29   showModal = () => {
 30     this.setState({
 31       visible: true,
 32     });
 33   }
 34
 35   /**
 36  * 确认
 37  *
 38  *  */
 39   handleOk = (e) => {
 40     const { imageSuccessCBK } = this.props
 41     const { listObj } = this.state
 42     const imagesFileArr = Object.keys(listObj).map(v => listObj[v])
 43     imageSuccessCBK(imagesFileArr)
 44     this.setState({
 45       visible: false,
 46       listObj: {},
 47       Uploading: false
 48     });
 49   }
 50
 51   handleCancel = (e) => {
 52     this.setState({
 53       visible: false,
 54       listObj: {}
 55     });
 56   }
 57   render() {
 58     const { loading } = this.props
 59     const { visible, listObj, Uploading } = this.state
 60     const props = {
 61       name: ‘file‘,
 62       multiple: true,
 63       action: ‘//jsonplaceholder.typicode.com/posts/‘,
 64       listType: ‘picture‘,
 65       onChange: (info) => {
 66         const uid = info.file.uid
 67         const objKeyArr = Object.keys(listObj)
 68         const status = info.file.status;
 69         if (status !== ‘uploading‘) {
 70           console.log(info.file, info.fileList);
 71         }
 72         if (status === ‘done‘) {//已成功上传
 73           this.setState({
 74             Uploading: false,
 75           })
 76           for (let i = 0, len = objKeyArr.length; i < len; i++) {
 77             if (listObj[objKeyArr[i]].uid === uid) {
 78               listObj[objKeyArr[i]].url = info.file.thumbUrl
 79               listObj[objKeyArr[i]].hasSuccess = true
 80               message.success(`${info.file.name} file uploaded successfully.`);
 81               return
 82             }
 83           }
 84
 85         } else if (status === ‘error‘) {
 86           this.setState({
 87             Uploading: false,
 88           })
 89           message.error(`${info.file.name} file upload failed.`);
 90         }
 91         if (status === ‘removed‘) {//移除上传的
 92           for (let i = 0, len = objKeyArr.length; i < len; i++) {
 93             if (listObj[objKeyArr[i]].uid === uid) {
 94               delete listObj[objKeyArr[i]]
 95               message.success(`${info.file.name} file removed successfully.`);
 96               return
 97             }
 98           }
 99         }
100       },
101       beforeUpload: (file) => {
102         this.setState({
103           Uploading: true,
104         })
105         const _self = this
106         const _URL = window.URL || window.webkitURL
107         const fileName = file.uid
108         listObj[fileName] = {}
109         return new Promise((resolve, reject) => {
110           const img = new Image()
111           img.src = _URL.createObjectURL(file)
112           img.onload = function () {
113             listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
114             _self.setState({
115               listObj,
116             })
117           }
118           resolve(true)
119         })
120       },
121     };
122
123     return (
124       <div>
125         <Button
126           style={{ marginTop: 0 }}
127           type="primary"
128           shape="round"
129           icon="upload"
130           onClick={() => { this.showModal() }}>
131           上传
132            </Button>
133         {
134           visible ? <Modal
135             title="上传图片"
136             visible={visible}
137             onCancel={this.handleCancel}
138             footer={[
139               <div key="1">
140                 <Button onClick={() => this.handleCancel()} loading={!!Uploading}>取消</Button>
141                 <Button type="primary" style={{ marginLeft: 8 }} onClick={() => this.handleOk()} loading={!!Uploading}>
142                   确定
143                 </Button>
144               </div>]}
145           >
146             <Dragger {...props}>
147               <p className="ant-upload-drag-icon">
148                 <Icon type="inbox" />
149               </p>
150               <p className="ant-upload-text">Click or drag file to this area to upload</p>
151               <p className="ant-upload-hint">Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</p>
152             </Dragger>
153           </Modal> : null
154         }
155       </div>
156     );
157   }
158 }
159
160 export default UploadImage;

2.3,Vue组件化

直接放代码

  1 <template>
  2   <div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container">
  3     <div>
  4     <textarea :id="tinymceId" class="tinymce-textarea" />
  5     </div>
  6     <div class="editor-custom-btn-container">
  7       <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
  8     </div>
  9   </div>
 10 </template>
 11
 12 <script>
 13 import editorImage from ‘./components/EditorImage‘
 14 import plugins from ‘./plugins‘
 15 import toolbar from ‘./toolbar‘
 16 import font from ‘./font‘;
 17
 18 export default {
 19   name: ‘Tinymce‘,
 20   components: { editorImage },
 21   props: {
 22     id: {
 23       type: String,
 24       default: function() {
 25         return ‘vue-tinymce-‘ + +new Date() + ((Math.random() * 1000).toFixed(0) + ‘‘)
 26       }
 27     },
 28     value: {
 29       type: String,
 30       default: ‘‘
 31     },
 32     toolbar: {
 33       type: Array,
 34       required: false,
 35       default() {
 36         return []
 37       }
 38     },
 39     menubar: {
 40       type: String,
 41       default: ‘file edit insert view format table‘
 42     },
 43     height: {
 44       type: Number,
 45       required: false,
 46       default: 520
 47     }
 48   },
 49   data() {
 50     return {
 51       hasChange: false,
 52       hasInit: false,
 53       tinymceId: this.id,
 54       fullscreen: false,
 55       languageTypeList: {
 56         ‘en‘: ‘en‘,
 57         ‘zh‘: ‘zh_CN‘
 58       }
 59     }
 60   },
 61   computed: {
 62     language() {
 63       return this.languageTypeList[this.$store.getters.language]
 64     }
 65   },
 66   watch: {
 67     value(val) {
 68       if (!this.hasChange && this.hasInit) {
 69         this.$nextTick(() =>
 70           window.tinymce.get(this.tinymceId).setContent(val || ‘‘))
 71       }
 72     },
 73     language() {
 74       this.destroyTinymce()
 75       this.$nextTick(() => this.initTinymce())
 76     }
 77   },
 78   mounted() {
 79     this.initTinymce()
 80   },
 81   activated() {
 82     this.initTinymce()
 83   },
 84   deactivated() {
 85     this.destroyTinymce()
 86   },
 87   destroyed() {
 88     this.destroyTinymce()
 89   },
 90   methods: {
 91     initTinymce() {
 92       const _this = this
 93       window.tinymce.init({
 94         language: ‘zh_CN‘,
 95         selector: `#${this.tinymceId}`,
 96         height: this.height,
 97         body_class: ‘panel-body ‘,
 98         object_resizing: false,
 99         toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
100         menubar: this.menubar,
101         plugins: plugins,
102         end_container_on_empty_block: true,
103         fontsize_formats: font.fontsizeFormats,
104         font_formats: font.fontFormats,
105         powerpaste_word_import: ‘clean‘,
106         code_dialog_height: 450,
107         code_dialog_width: 1000,
108         advlist_bullet_styles: ‘square‘,
109         advlist_number_styles: ‘default‘,
110         imagetools_cors_hosts: [‘www.tinymce.com‘, ‘codepen.io‘],
111         default_link_target: ‘_blank‘,
112         link_title: false,
113         nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
114         init_instance_callback: editor => {
115           if (_this.value) {
116             editor.setContent(_this.value)
117           }
118           _this.hasInit = true
119           editor.on(‘NodeChange Change KeyUp SetContent‘, () => {
120             this.hasChange = true
121             this.$emit(‘input‘, editor.getContent())
122           })
123         },
124         setup(editor) {
125           editor.on(‘FullscreenStateChanged‘, (e) => {
126             _this.fullscreen = e.state
127           })
128         }
129       })
130     },
131     destroyTinymce() {
132       const tinymce = window.tinymce.get(this.tinymceId)
133       if (this.fullscreen) {
134         tinymce.execCommand(‘mceFullScreen‘)
135       }
136
137       if (tinymce) {
138         tinymce.destroy()
139       }
140     },
141     setContent(value) {
142       window.tinymce.get(this.tinymceId).setContent(value)
143     },
144     getContent() {
145       window.tinymce.get(this.tinymceId).getContent()
146     },
147     imageSuccessCBK(arr) {
148       const _this = this
149       arr.forEach(v => {
150         window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
151       })
152     }
153   }
154 }
155 </script>
156
157 <style scoped>
158 .tinymce-container {
159   position: relative;
160   line-height: normal;
161 }
162 .tinymce-container>>>.mce-fullscreen {
163   z-index: 10000;
164 }
165 .tinymce-textarea {
166   visibility: hidden;
167   z-index: -1;
168 }
169 .editor-custom-btn-container {
170   position: absolute;
171   right: 4px;
172   top: 4px;
173   /*z-index: 2005;*/
174 }
175 .fullscreen .editor-custom-btn-container {
176   z-index: 10000;
177   position: fixed;
178 }
179 .editor-upload-btn {
180   display: inline-block;
181 }
182 </style>

上传图片组件,使用elementUI的部分组件:

  1 <template>
  2   <div class="upload-container">
  3     <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
  4       upload
  5     </el-button>
  6     <el-dialog :visible.sync="dialogVisible">
  7       <el-upload
  8         :multiple="true"
  9         :file-list="fileList"
 10         :show-file-list="true"
 11         :on-remove="handleRemove"
 12         :on-success="handleSuccess"
 13         :before-upload="beforeUpload"
 14         class="editor-slide-upload"
 15         action="https://httpbin.org/post"
 16         list-type="picture-card"
 17       >
 18         <el-button size="small" type="primary">
 19           Click upload
 20         </el-button>
 21       </el-upload>
 22       <el-button @click="dialogVisible = false">
 23         Cancel
 24       </el-button>
 25       <el-button type="primary" @click="handleSubmit">
 26         Confirm
 27       </el-button>
 28     </el-dialog>
 29   </div>
 30 </template>
 31
 32 <script>
 33 // import { getToken } from ‘api/qiniu‘
 34
 35 export default {
 36   name: ‘EditorSlideUpload‘,
 37   props: {
 38     color: {
 39       type: String,
 40       default: ‘#1890ff‘
 41     }
 42   },
 43   data() {
 44     return {
 45       dialogVisible: false,
 46       listObj: {},
 47       fileList: []
 48     }
 49   },
 50   methods: {
 51     checkAllSuccess() {
 52       return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
 53     },
 54     handleSubmit() {
 55       const arr = Object.keys(this.listObj).map(v => this.listObj[v])
 56       if (!this.checkAllSuccess()) {
 57         this.$message(‘Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!‘)
 58         return
 59       }
 60       this.$emit(‘successCBK‘, arr)
 61       this.listObj = {}
 62       this.fileList = []
 63       this.dialogVisible = false
 64     },
 65     handleSuccess(response, file) {
 66       const uid = file.uid
 67       const objKeyArr = Object.keys(this.listObj)
 68       for (let i = 0, len = objKeyArr.length; i < len; i++) {
 69         if (this.listObj[objKeyArr[i]].uid === uid) {
 70           this.listObj[objKeyArr[i]].url = response.files.file
 71           this.listObj[objKeyArr[i]].hasSuccess = true
 72           return
 73         }
 74       }
 75     },
 76     handleRemove(file) {
 77       const uid = file.uid
 78       const objKeyArr = Object.keys(this.listObj)
 79       for (let i = 0, len = objKeyArr.length; i < len; i++) {
 80         if (this.listObj[objKeyArr[i]].uid === uid) {
 81           delete this.listObj[objKeyArr[i]]
 82           return
 83         }
 84       }
 85     },
 86     beforeUpload(file) {
 87       const _self = this
 88       const _URL = window.URL || window.webkitURL
 89       const fileName = file.uid
 90       this.listObj[fileName] = {}
 91       return new Promise((resolve, reject) => {
 92         const img = new Image()
 93         img.src = _URL.createObjectURL(file)
 94         img.onload = function() {
 95           _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
 96         }
 97         resolve(true)
 98       })
 99     }
100   }
101 }
102 </script>
103
104 <style lang="scss" scoped>
105 .editor-slide-upload {
106   margin-bottom: 20px;
107   /deep/ .el-upload--picture-card {
108     width: 100%;
109   }
110 }
111 </style>

三,使用

3.1,React

第一步:导入组件

1 import Tinymce from ‘../../components/Tinymce‘;

第二步:使用组件

1   <Tinymce
2             content={‘‘}
3               tinymceId=‘tinymceIdDemo‘
4               getContent={(content) => { this.getContent(content) }}
5             />

第三步:获取输入的富文本

1  getContent(content) {
2     console.log(‘content===‘,content)
3     this.setState({
4       content
5     })
6   }

第四步:文本渲染

1  {/* 渲染标签字符串 */}
2             <div dangerouslySetInnerHTML={{ __html: content }}></div>

第五步:效果

图片上传成功效果

完成效果展示

3.2,Vue

第一步:引入组件

1 import Tinymce from ‘@/components/Tinymce‘

第二步:注册组件

1   components: { Tinymce },

第三步:使用组件

1  <div>
2       <tinymce v-model="content" :height="300" />
3     </div>

第四步:获取内容

利用vue的数据双向绑定

1  getContent() {
2       console.log(this.content)
3       this.hasContent = this.content
4     }

第五步:渲染获取的内容

1 <div class="editor-content" v-html="hasContent" />

第六步:效果图

图片上传成功效果

完整效果

原文地址:https://www.cnblogs.com/jackson-zhangjiang/p/10782220.html

时间: 2024-08-28 07:20:05

富文本编辑器TinyMCE的使用(React Vue)的相关文章

react引入富文本编辑器TinyMCE

这周做了一个PC端的service后台需求,要求有富文本编辑器,插入图片.表格,字体字号背景色等等, 最后引入了富文本编辑器TinyMCE 对于TinyMCE的简介: TinyMCE是一款易用.且功能强大的所见即所得的富文本编辑器.同类程序有:UEditor.Kindeditor.Simditor.CKEditor.wangEditor.Suneditor.froala等等. TinyMCE的优势: 开源可商用,基于LGPL2.1 插件丰富,自带插件基本涵盖日常所需功能 接口丰富,可扩展性强,有

django后台集成富文本编辑器Tinymce的使用

富文本编辑器Tinymce是使用步骤: 1.首先去python的模块包的网站下载一个django-tinymce的包 2.下载上图的安装包,然后解压,进入文件夹,执行: (pychrm直接运行命令pip install django_tinymce直接下载安装) 安装完成之后,就可以在C:\python3.6\Lib\site-packages这个文件夹. 点击进入文件夹,copy"tinymce"这个文件到你的项目的根目录下. 3.配置tinycmce到你的项目中,配置到admin数

Java开发之富文本编辑器TinyMCE

一.题外话 最近负责了一个cms网站的运维,里面存在很多和编辑器有关的问题,比如编辑一些新闻博客,论文模块.系统采用的是FCKEditor,自我感觉不是很好,如下图 特别是在用户想插入一个图片的话,就很麻烦,所有用户共享一个文件目录,这样就不好了,于是便想到了TinyMCE编辑器,博客园默认的也是这个编辑器,接下 来,我们开始吧 二.TinyMCE编辑器集成步骤 2.1:下载相关文件 (1)下载TinyMCE插件包 下载地址:   https://www.tinymce.com/download

在vue中使用tinymce富文本编辑器,解决tinymce在dialog对话框中层级太低的问题

1.安装 npm install tinymce -S 2.把node_modules\tinymce里面的文件 包括tinymce文件夹 全部复制到static文件夹下面,如下图 3.tinymce默认是英文界面,还需要下载一个中文语言包zh_CN.js https://www.tiny.cloud/get-tiny/language-packages/ 在tinymce文件夹下新建langs文件夹,将下载好的语言包放到langs文件夹下面如图  4.在main.js中引入tinymce  5

tinymce 富文本编辑器 编写资料

tinymce官方文档: 粘贴图片插件 博客搬运地址 使用Blob获取图片并二进制显示实例页面 tinymce自动调整插件 是时候掌握一个富文本编辑器了——TinyMCE(1) XMLHttpRequest 2.0的家臣们 URL.createObjectURL和URL.revokeObjectURL 如何使用 blob:http:// 隐藏一个mp3文件的真实地址? https://www.z-u-i-d-a-i-m-a.com/question/3913274460982272.htm 使用

各大富文本编辑器对比

各大WYSIWYG编辑器的简单比较 TinyMCE强大的富文本编辑器,支持拖拽.粘贴上传图片,支持拖拽表格.博客园用的就是这个编辑器. 参考博客:1.   https://segmentfault.com/a/1190000012791569#articleHeader8   vue项目移植tinymce踩坑 2. https://blog.csdn.net/haoxiaoyong1014/article/details/82683428   Vue 自定义富文本编辑器 tinymce 支持导入

在 Vue 项目中引入 tinymce 富文本编辑器

项目中原本使用的富文本编辑器是 wangEditor,这是一个很轻量.简洁编辑器 但是公司的业务升级,想要一个功能更全面的编辑器,我找了好久,目前常见的编辑器有这些: UEditor:百度前端的开源项目,功能强大,基于 jQuery,但已经没有再维护,而且限定了后端代码,修改起来比较费劲 bootstrap-wysiwyg:微型,易用,小而美,只是 Bootstrap + jQuery... kindEditor:功能强大,代码简洁,需要配置后台,而且好久没见更新了 wangEditor:轻量.

vue中引入Tinymce富文本编辑器

最近想在项目上引入一个富文本编辑器,之前引入过summernote,感觉并不太适合vue使用, 然后在网上查了查,vue中使用Tinymce比较适合, 首先,我们在vue项目的components文件夹中加入如下几个文件 首先看一下Tinymce/dynamicLoadScript.js的内容: let callbacks = [] function loadedTinymce() { // to fixed https://github.com/PanJiaChen/vue-element-a

在vue cli 3脚手架里引入tinymce 5富文本编辑器

本文主要讲的是在Vue cli 3脚手架搭建的项目里如何引用Tinymce 5富文本编辑器. 请注意识别"版本号",不同版本的配置细节有所不同. 1. tinymce的安装 1. 安装tinymce-vue npm install @tinymce/tinymce-vue -S 2. 安装tinymce npm install tinymce -S 3. 下载中文语言包 tinymce提供的语言包很多,选择下载中文语言包 2. 使用方法 1. 文件操作 在项目根目录的public目录下