editor.md 13.6 KB
Newer Older
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#### editor

富文本编辑器,可以对图片、文字格式进行编辑和混排。

在web开发时,可以使用`contenteditable`来实现内容编辑。但这是一个dom API,在非H5平台无法使用。于是微信小程序和uni-app的App-vue提供了`editor`组件来实现这个功能,并且在uni-app的H5平台也提供了兼容。从技术本质来讲,这个组件仍然运行在视图层webview中,利用的也是浏览器的`contenteditable`功能。

编辑器导出内容支持带标签的 `html`和纯文本的 `text`,编辑器内部采用 `delta` 格式进行存储。

通过`setContents`接口设置内容时,解析插入的 `html` 可能会由于一些非法标签导致解析错误,建议开发者在应用内使用时通过 delta 进行插入。

富文本组件内部引入了一些基本的样式使得内容可以正确的展示,开发时可以进行覆盖。需要注意的是,在其它组件或环境中使用富文本组件导出的html时,需要额外引入[这段样式](https://github.com/dcloudio/uni-app/blob/master/src/core/view/components/editor/editor.css),并维护`<ql-container><ql-editor></ql-editor></ql-container>`的结构,参考:[使用 editor 组件导出的 html](https://ask.dcloud.net.cn/article/36205)

图片控件仅初始化时设置有效。

相关 api:[editorContext](/api/media/editor-context)

**平台差异说明**

19
|App					|H5			|微信小程序		|支付宝小程序|百度小程序		|抖音小程序、飞书小程序|QQ小程序		|快应用					|360小程序|快手小程序	|
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
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
|:-:					|:-:		|:-:					|:-:		    |:-:					|:-:					|:-:				|:-:						|:-:			|:-:			|
|2.0+,app-vue|2.4.5+	|基础库 2.7.0+|x						|需引入动态库[引入方式](https://smartprogram.baidu.com/docs/develop/framework/dynamiclib_use/)					|x							|x				|x				|x				|x				|

editor组件目前只有H5、App的vue页面、微信小程序、百度小程序支持,其他端平台自身未提供editor组件,只能使用web-view加载web页面,也可搜索[插件市场](https://ext.dcloud.net.cn/search?q=%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91) 获取简单的markdown富文本编辑器

| 属性 | 类型 | 默认值 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| read-only | boolean | false | 否 | 设置编辑器为只读 |
| placeholder | string |  | 否 | 提示信息 |
| show-img-size | boolean | false | 否 | 点击图片时显示图片大小控件 |
| show-img-toolbar | boolean | false | 否 | 点击图片时显示工具栏控件 |
| show-img-resize | boolean | false | 否 | 点击图片时显示修改尺寸控件 |
| @ready | eventhandle |  | 否 | 编辑器初始化完成时触发 |
| @focus | eventhandle |  | 否 | 编辑器聚焦时触发,event.detail = {html, text, delta} |
| @blur | eventhandle |  | 否 | 编辑器失去焦点时触发,detail = {html, text, delta} |
| @input | eventhandle |  | 否 | 编辑器内容改变时触发,detail = {html, text, delta} |
| @statuschange | eventhandle |  | 否 | 通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式 |

编辑器内支持部分 HTML 标签和内联样式,不支持**class****id**

#### 支持的标签

不满足的标签会被忽略,`<div>`会被转行为`<p>`储存。

| 类型 | 节点 |平台差异说明 |
| --- | --- |--- |
| 行内元素 | `<span> <strong> <b> <ins> <em> <i> <u> <a> <del> <s> <sub> <sup> <img>` |其中`<ins>  <del> `百度小程序不支持 |
| 块级元素 | `<br> <p> <h1> <h2> <h3> <h4> <h5> <h6> <hr> <ol> <ul> <li>` |其中`<br>`仅百度小程序支持、`<p>`百度小程序不支持|

#### 支持的内联样式

内联样式仅能设置在行内元素或块级元素上,不能同时设置。例如 font-size` 归类为行内元素属性,在 p 标签上设置是无效的。

| 类型 | 样式 |平台差异说明 |
| --- | --- |--- |
| 块级样式 | `text-align` `direction` `margin` `margin-top` `margin-left` `margin-right` `margin-bottom` `padding` `padding-top` `padding-left` `padding-right` `padding-bottom` `line-height` `text-indent` |百度小程序仅支持`text-align、direction`|
| 行内样式 | `font` `font-size` `font-style` `font-variant` `font-weight` `font-family` `letter-spacing` `text-decoration` `color` `background-color` |百度小程序仅支持`color、background-color`|

**注意事项**

* 插入的 html 中事件绑定会被移除
* formats 中的 color 属性会统一以 hex 格式返回
* 粘贴时仅纯文本内容会被拷贝进编辑器
* 插入 html 到编辑器内时,编辑器会删除一些不必要的标签,以保证内容的统一。例如`<p><span>xxx</span></p>`会改写为`<p>xxx</p>`
* 编辑器聚焦时页面会被上推,系统行为以保证编辑区可见
W
wanganxp 已提交
65
* H5端会动态引入依赖 [quill.min.js](https://unpkg.com/quill@1.3.7/dist/quill.min.js)、[image-resize.min.js](https://unpkg.com/quill-image-resize-mp@3.0.1/image-resize.min.js),依赖从 [unpkg.com](https://unpkg.com) 加载,如过依赖加载较慢或失败,可以从 [github.com](https://github.com/dcloudio/uni-app/tree/dev/src/core/view/components/editor/quill) 或者 [gitee.com](https://gitee.com/dcloud/uni-app/tree/dev/src/core/view/components/editor/quill) 下载下来放在自己的服务器或 CDN 服务商,相比自己的服务器或者其他 CDN 服务商更推荐的做法是开发者将将所有前端资源托管在 [uniCloud 前端网页托管](https://doc.dcloud.net.cn/uniCloud/hosting.html) ,然后在 [自定义模板](../collocation/manifest?id=h5-template) 的 `head` 标签内引入。
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
66 67 68 69 70 71
* 不能直接插入视频或者其他文件,编辑时可以采用视频封面或者文件缩略图占位,并在图片属性中保存视频信息,预览时读取附加信息再还原为视频或者其他文件操作。


**示例代码** [查看演示](https://hellouniapp.dcloud.net.cn/pages/component/editor/editor)

::: preview https://hellouniapp.dcloud.net.cn/pages/component/editor/editor
biscuits_cat's avatar
biscuits_cat 已提交
72
示例中css文件地址https://github.com/dcloudio/hello-uniapp/blob/master/pages/component/editor/editor-icon.css
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
73 74 75 76
> Template
``` vue
<template>
	<view class="container">
biscuits_cat's avatar
biscuits_cat 已提交
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
		<view class="page-body">
			<view class='wrapper'>
				<view class='toolbar' @tap="format" style="height: 120px;overflow-y: auto;">
					<view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold">
					</view>
					<view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic">
					</view>
					<view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian"
						data-name="underline"></view>
					<view :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian"
						data-name="strike"></view>
					<!-- #ifndef MP-BAIDU -->
					<view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi"
						data-name="align" data-value="left"></view>
					<!-- #endif -->
					<view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi"
						data-name="align" data-value="center"></view>
					<view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi"
						data-name="align" data-value="right"></view>
					<view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi"
						data-name="align" data-value="justify"></view>
					<!-- #ifndef MP-BAIDU -->
					<view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-line-height"
						data-name="lineHeight" data-value="2"></view>
					<view :class="formats.letterSpacing ? 'ql-active' : ''" class="iconfont icon-Character-Spacing"
						data-name="letterSpacing" data-value="2em"></view>
					<view :class="formats.marginTop ? 'ql-active' : ''" class="iconfont icon-722bianjiqi_duanqianju"
						data-name="marginTop" data-value="20px"></view>
					<view :class="formats.marginBottom ? 'ql-active' : ''" class="iconfont icon-723bianjiqi_duanhouju"
						data-name="marginBottom" data-value="20px"></view>
					<!-- #endif -->

					<view class="iconfont icon-clearedformat" @tap="removeFormat"></view>

					<!-- #ifndef MP-BAIDU -->
					<view :class="formats.fontFamily ? 'ql-active' : ''" class="iconfont icon-font"
						data-name="fontFamily" data-value="Pacifico"></view>
					<view :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize"
						data-name="fontSize" data-value="24px"></view>
					<!-- #endif -->
					<view :class="formats.color === '#0000ff' ? 'ql-active' : ''" class="iconfont icon-text_color"
						data-name="color" data-value="#0000ff"></view>
					<view :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
						class="iconfont icon-fontbgcolor" data-name="backgroundColor" data-value="#00ff00"></view>
					<view class="iconfont icon-date" @tap="insertDate"></view>
					<view class="iconfont icon--checklist" data-name="list" data-value="check"></view>
					<view :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie"
						data-name="list" data-value="ordered"></view>
					<view :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie"
						data-name="list" data-value="bullet"></view>

					<view class="iconfont icon-undo" @tap="undo"></view>
					<view class="iconfont icon-redo" @tap="redo"></view>

					<view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
					<view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
					<view class="iconfont icon-fengexian" @tap="insertDivider"></view>
					<view class="iconfont icon-charutupian" @tap="insertImage"></view>
					<view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1"
						data-name="header" :data-value="1"></view>
					<view :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao"
						data-name="script" data-value="sub"></view>
					<view :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao"
						data-name="script" data-value="super"></view>

					<view class="iconfont icon-shanchu" @tap="clear"></view>

					<view :class="formats.direction === 'rtl' ? 'ql-active' : ''" class="iconfont icon-direction-rtl"
						data-name="direction" data-value="rtl"></view>
				</view>

				<view class="editor-wrapper">
					<editor id="editor" class="ql-container" placeholder="开始输入..." show-img-size show-img-toolbar
						show-img-resize @statuschange="onStatusChange" :read-only="readOnly" @ready="onEditorReady">
					</editor>
				</view>
			</view>
		</view>
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
155 156 157 158 159 160 161 162 163
	</view>
</template>
``` 
> Script
``` vue
<script>
	export default {
		data() {
			return {
biscuits_cat's avatar
biscuits_cat 已提交
164 165
				readOnly: false,
				formats: {}
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
166 167
			}
		},
biscuits_cat's avatar
biscuits_cat 已提交
168 169 170 171 172 173 174 175
		onLoad() {
			// #ifndef MP-BAIDU 
			uni.loadFontFace({
				family: 'Pacifico',
				source: 'url("https://sungd.github.io/Pacifico.ttf")'
			})
			// #endif
		},
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
176
		methods: {
biscuits_cat's avatar
biscuits_cat 已提交
177 178 179
			readOnlyChange() {
				this.readOnly = !this.readOnly
			},
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
180
			onEditorReady() {
biscuits_cat's avatar
biscuits_cat 已提交
181 182 183 184 185 186 187 188 189
				// #ifdef MP-BAIDU
				this.editorCtx = requireDynamicLib('editorLib').createEditorContext('editor');
				// #endif

				// #ifdef APP-PLUS || MP-WEIXIN || H5
				uni.createSelectorQuery().select('#editor').context((res) => {
					this.editorCtx = res.context
				}).exec()
				// #endif
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
190 191 192
			},
			undo() {
				this.editorCtx.undo()
biscuits_cat's avatar
biscuits_cat 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
			},
			redo() {
				this.editorCtx.redo()
			},
			format(e) {
				let {
					name,
					value
				} = e.target.dataset
				if (!name) return
				// console.log('format', name, value)
				this.editorCtx.format(name, value)
			},
			onStatusChange(e) {
				const formats = e.detail
				this.formats = formats
			},
			insertDivider() {
				this.editorCtx.insertDivider({
					success: function() {
						console.log('insert divider success')
					}
				})
			},
			clear() {
				uni.showModal({
					title: '清空编辑器',
					content: '确定清空编辑器全部内容?',
					success: res => {
						if (res.confirm) {
							this.editorCtx.clear({
								success: function(res) {
									console.log("clear success")
								}
							})
						}
					}
				})
			},
			removeFormat() {
				this.editorCtx.removeFormat()
			},
			insertDate() {
				const date = new Date()
				const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
				this.editorCtx.insertText({
					text: formatDate
				})
			},
			insertImage() {
				uni.chooseImage({
					count: 1,
					success: (res) => {
						this.editorCtx.insertImage({
							src: res.tempFilePaths[0],
							alt: '图像',
							success: function() {
								console.log('insert image success')
							}
						})
					}
				})
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
255 256 257 258 259 260 261 262
			}
		}
	}
</script>
``` 
> Style
``` vue
<style>
biscuits_cat's avatar
biscuits_cat 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
	@import "./editor-icon.css";

	.page-body {
		height: calc(100vh - var(--window-top) - var(--status-bar-height));
	}

	.wrapper {
		height: 100%;
	}

	.editor-wrapper {
		height: calc(100vh - var(--window-top) - var(--status-bar-height) - 140px);
		background: #fff;
	}

	.iconfont {
		display: inline-block;
		padding: 8px 8px;
		width: 24px;
		height: 24px;
		cursor: pointer;
		font-size: 20px;
	}

	.toolbar {
		box-sizing: border-box;
		border-bottom: 0;
		font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
291 292
	}

biscuits_cat's avatar
biscuits_cat 已提交
293 294 295
	.ql-container {
		box-sizing: border-box;
		padding: 12px 15px;
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
296
		width: 100%;
biscuits_cat's avatar
biscuits_cat 已提交
297 298 299 300 301
		min-height: 30vh;
		height: 100%;
		margin-top: 20px;
		font-size: 16px;
		line-height: 1.5;
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
302 303
	}

biscuits_cat's avatar
biscuits_cat 已提交
304 305
	.ql-active {
		color: #06c;
DCloud_Heavensoft's avatar
DCloud_Heavensoft 已提交
306 307 308 309 310
	}
</style>
```
:::

W
wanganxp 已提交
311
一个完整的内容管理系统比较复杂,DCloud已提供好了现成的`uni-cms`,开源、免费、全端可用、功能完善,包括管理端和用户端。还内置了ai生成文章和广告变现功能。详见:[https://doc.dcloud.net.cn/uniCloud/uni-cms.html](https://doc.dcloud.net.cn/uniCloud/uni-cms.html)