一,需求與介紹
1.1,需求
? ? 編輯新聞等富有個(gè)性化的文本
1.2,介紹
TinyMCE是一款易用、且功能強(qiáng)大的所見即所得的富文本編輯器蚪缀。
TinyMCE的優(yōu)勢:
開源可商用秫逝,基于LGPL2.1
插件豐富,自帶插件基本涵蓋日常所需功能
接口豐富询枚,可擴(kuò)展性強(qiáng)违帆,有能力可以無限拓展功能
界面好看,符合現(xiàn)代審美
提供經(jīng)典金蜀、內(nèi)聯(lián)刷后、沉浸無干擾三種模式
對標(biāo)準(zhǔn)支持優(yōu)秀(自v5開始)
多語言支持,官網(wǎng)可下載幾十種語言渊抄。
二,配置集成并組件化
2.1,通用配置
1,工具欄toolbar
復(fù)制代碼
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
復(fù)制代碼
2,插件plugins
復(fù)制代碼
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
復(fù)制代碼
3,常用字體配置fonts
復(fù)制代碼
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 }
復(fù)制代碼
4,準(zhǔn)備標(biāo)簽
1? <div>
2? ? ? ? ? ? ? <textarea id="tinymceId"/>
3? ? ? ? ? ? </div>
在textarea外面需要套一層div,否則會(huì)產(chǎn)生一些意想不到的問題
5,初始化標(biāo)簽,生成編輯框
復(fù)制代碼
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 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? ? })
復(fù)制代碼
2.2,React組件化
直接放代碼
復(fù)制代碼
? 1 import React from 'react';
? 2 import { Button } from 'antd';
? 3 import PropTypes from 'prop-types';
? 4 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 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? ? * 上傳圖片成功回調(diào)
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={['富文本實(shí)例']} title={'富文本實(shí)例'} />
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;
復(fù)制代碼
上傳圖片組件,使用antd的部分組件:
復(fù)制代碼
? 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? * 確認(rèn)
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;
復(fù)制代碼
2.3,Vue組件化
? 直接放代碼
復(fù)制代碼
? 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 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>
復(fù)制代碼
上傳圖片組件,使用elementUI的部分組件:
復(fù)制代碼
? 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>
復(fù)制代碼
三,使用
3.1,React
第一步:導(dǎo)入組件
1 import Tinymce from '../../components/Tinymce';
第二步:使用組件
1? <Tinymce
2? ? ? ? ? ? content={''}
3? ? ? ? ? ? ? tinymceId='tinymceIdDemo'
4? ? ? ? ? ? ? getContent={(content) => { this.getContent(content) }}
5? ? ? ? ? ? />
第三步:獲取輸入的富文本
復(fù)制代碼
1? getContent(content) {
2? ? console.log('content===',content)
3? ? this.setState({
4? ? ? content
5? ? })
6? }
復(fù)制代碼
第四步:文本渲染
1? {/* 渲染標(biāo)簽字符串 */}
2? ? ? ? ? ? <div dangerouslySetInnerHTML={{ __html: content }}></div>
東莞網(wǎng)站建設(shè)www.zg886.cn