富文本编辑器tinymce

tinytmce是一款功能强大的富文本编辑器,插件丰富。支持typescript。易于接入vue、react

依赖安装

.npmrc 镜像配置

1
2
3
4
5
6
7
8
9
home=https://npm.taobao.org
registry=https://registry.npm.taobao.org/
sass_binary_site="https://npm.taobao.org/mirrors/node-sass/"
cypress_download_mirror="https://npm.taobao.org/mirrors/cypress/"
phantomjs_cdnurl="http://cnpmjs.org/downloads"
electron_mirror="https://npm.taobao.org/mirrors/electron/"
sqlite3_binary_host_mirror="https://foxgis.oss-cn-shanghai.aliyuncs.com/"
profiler_binary_host_mirror="https://npm.taobao.org/mirrors/node-inspector/"
chromedriver_cdnurl="https://cdn.npm.taobao.org/dist/chromedriver"

安装tinytmce

1
2
# 其中@types/tinymce为ts声明,不用ts可移除掉
npm install tinymce @types/tinymce @tinymce/tinymce-vue --save

静态文件

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# component
<template>
<div
class="tinymce-container"
:class="{fullscreen: fullscreen}"
:style="{width: containerWidth}">
<editor
:id="id"
:ref="id"
v-model="content"
:init="initOptions"
/>
</div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'

// Docs: https://www.tiny.cloud/docs/advanced/usage-with-module-loaders/
// Import TinyMCE
import 'tinymce/tinymce'
// Default icons are required for TinyMCE 5.3 or above
import 'tinymce/icons/default'
// Import themes
import 'tinymce/themes/silver'
import 'tinymce/themes/mobile'
// Any plugins you want to use has to be imported
import 'tinymce/plugins/advlist'
import 'tinymce/plugins/anchor'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/code'
import 'tinymce/plugins/codesample'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/emoticons'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/hr'
import 'tinymce/plugins/image'
import 'tinymce/plugins/imagetools'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/link'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/media'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/noneditable'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/paste'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/print'
import 'tinymce/plugins/save'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/spellchecker'
import 'tinymce/plugins/tabfocus'
import 'tinymce/plugins/table'
import 'tinymce/plugins/template'
import 'tinymce/plugins/textpattern'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/wordcount'

import Editor from '@tinymce/tinymce-vue'

@Component({ name: 'TinymceEditor', components: { Editor } })
export default class extends Vue {
// 传入的富文本字符串
@Prop({ default: '' }) value!: string
@Prop({ default: `${+new Date()}` }) private id!: string
@Prop({ default: '360px' }) private height!: string | number
@Prop({ default: 'auto' }) private width!: string | number

private hasChange = false
private hasInit = false
private fullscreen = false

// 富文本框值
get content() {
return this.value
}

// 监听值改变并$emit,同@input监听
set content(content) {
this.$emit('input', content)
}

get containerWidth() {
const width = this.width
// Test matches `100`, `'100'`
if (/^[\d]+(\.[\d]+)?$/.test(width.toString())) {
return `${width}px`
}
return width
}

// 富文本框init配置
private get initOptions() {
return {
selector: `#${this.id}`,
height: this.height,
body_class: 'tinymce-body ', // 富文本body上增加class
language: 'zh_CN',
language_url: `${process.env.BASE_URL}tinymce/langs/zh_CN.js`, // process.env.BASE_URL指向public路径
skin_url: `${process.env.BASE_URL}tinymce/skins/ui/oxide`,
emoticons_database_url: `${process.env.BASE_URL}tinymce/emojis.min.js`,
menubar: 'file edit insert view format table tools help', // true, false, or string of menus
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'], // 插件
toolbar: ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote hr pagebreak subscript superscript removeformat emoticons link image media charmap anchor bullist numlist insertdatetime table forecolor backcolor', '| fontselect | fontsizeselect | formatselect | code codesample | undo redo | preview fullscreen'], // 工具条
font_formats: '微软雅黑=Microsoft Yahei; Arial=arial,helvetica,sans-serif; 宋体=SimSun; Impact=impact,chicago;', // 字体
fontsize_formats: '11px 12px 14px 16px 18px 24px 36px 48px 64px 72px', // 文字大小
codesample_languages: [
{ text: 'HTML/XML', value: 'markup' },
{ text: 'JavaScript', value: 'javascript' },
{ text: 'CSS', value: 'css' },
{ text: 'Java', value: 'java' },
{ text: 'C++', value: 'cpp' }
],
object_resizing: false,
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',
default_link_target: '_blank',
link_title: false,
nonbreaking_force_tab: true,
convert_urls: false,
branding: false,
paste_data_images: true,
// 图片上传回调
images_upload_handler: (blobInfo: any, success: any) => {
const file = blobInfo.blob()
console.log(file)
// this.$api.common.aliyunUpload(file, 'donggu').then((url: any) => {
// success(url)
// })
},
// 初始化回调
init_instance_callback: (editor: any) => {
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: any) => {
editor.on('FullscreenStateChanged', (e: any) => {
this.fullscreen = e.state
})
}
}
}
}
</script>

<style lang="scss" scoped></style>

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<TinymceEditor :value="'这是默认富文本信息'" :id="'tinymceE'" :height="720" :width="'100%'" />
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import TinymceEditor from '@/components/TinymceEditor/index.vue'
@Component({
components: {
TinymceEditor
}
})
export default class extends Vue {
}
</script>

参考资料

https://www.tiny.cloud/docs/integrations/vue/#tinymcevuejsintegrationquickstartguide
https://github.com/Armour/vue-typescript-admin-template/blob/master/src/components/Tinymce/index.vue
https://yezipi.net/article/detail/10077
https://www.cnblogs.com/ze-hua/p/12171683.html
https://recomm.cnblogs.com/blogpost/11492259


富文本编辑器tinymce
http://example.com/20200806-tinymce/
作者
csorz
发布于
2020年8月6日
许可协议