富文本編輯器TinyMCE的使用(React Vue)

一,需求與介紹

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 &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? ? })

復(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 &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? ? * 上傳圖片成功回調(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 &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>

復(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尝胆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子护桦,更是在濱河造成了極大的恐慌含衔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件二庵,死亡現(xiàn)場離奇詭異贪染,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)催享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門抑进,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人睡陪,你說我怎么就攤上這事寺渗∧淝椋” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵信殊,是天一觀的道長炬称。 經(jīng)常有香客問我,道長涡拘,這世上最難降的妖魔是什么玲躯? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮鳄乏,結(jié)果婚禮上跷车,老公的妹妹穿的比我還像新娘。我一直安慰自己橱野,他們只是感情好朽缴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著水援,像睡著了一般密强。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜗元,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天或渤,我揣著相機(jī)與錄音,去河邊找鬼奕扣。 笑死薪鹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惯豆。 我是一名探鬼主播池磁,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼循帐!你這毒婦竟也來了框仔?” 一聲冷哼從身側(cè)響起舀武,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拄养,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后银舱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘪匿,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年寻馏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棋弥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诚欠,死狀恐怖顽染,靈堂內(nèi)的尸體忽然破棺而出漾岳,到底是詐尸還是另有隱情,我是刑警寧澤粉寞,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布尼荆,位于F島的核電站,受9級(jí)特大地震影響唧垦,放射性物質(zhì)發(fā)生泄漏捅儒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一振亮、第九天 我趴在偏房一處隱蔽的房頂上張望巧还。 院中可真熱鬧,春花似錦坊秸、人聲如沸麸祷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摇锋。三九已至,卻和暖如春站超,著一層夾襖步出監(jiān)牢的瞬間荸恕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工死相, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留融求,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓算撮,卻偏偏與公主長得像生宛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子肮柜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容