uniapp - editor 富文本的使用

1,473 阅读7分钟

uniapp - editor 富文本的使用

目录

[TOC]

editorContext.getContents(OBJECT)

完整例子

editor 组件

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

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

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

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

富文本组件内部引入了一些基本的样式使得内容可以正确的展示,开发时可以进行覆盖。需要注意的是,在其它组件或环境中使用富文本组件导出的html时,需要额外引入 这段样式 ,并维护 <ql-container><ql-editor></ql-editor></ql-container> 的结构,参考: 使用 editor 组件导出的 html

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

相关 api: editorContext

属性说明

属性类型默认值必填说明
read-onlybooleanfalse设置编辑器为只读
placeholderstring提示信息
show-img-sizebooleanfalse点击图片时显示图片大小控件
show-img-toolbarbooleanfalse点击图片时显示工具栏控件
show-img-resizebooleanfalse点击图片时显示修改尺寸控件
@readyeventhandle编辑器初始化完成时触发
@focuseventhandle编辑器聚焦时触发,event.detail = {html, text, delta}
@blureventhandle编辑器失去焦点时触发,detail = {html, text, delta}
@inputeventhandle编辑器内容改变时触发,detail = {html, text, delta}
@statuschangeeventhandle通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式

editorContext

editor 组件对应的 editorContext 实例,可通过 uni.createSelectorQuery 获取。

uni.createSelectorQuery()

返回一个 SelectorQuery 对象实例。可以在这个实例上使用 select 等方法选择节点,并使用 boundingClientRect 等方法选择需要查询的信息。

Tips:

  • 使用 uni.createSelectorQuery() 需要在生命周期 mounted 后进行调用。
  • 默认需要使用到 selectorQuery.in 方法。

SelectorQuery

查询节点信息的对象

selectorQuery.in(component)

将选择器的选取范围更改为自定义组件 component 内,返回一个 SelectorQuery 对象实例。(初始时,选择器仅选取页面范围的节点,不会选取任何自定义组件中的节点)。

代码示例

const query = uni.createSelectorQuery().in(this);
query
  .select("#id")
  .boundingClientRect((data) => {
    console.log("得到布局位置信息" + JSON.stringify(data));
    console.log("节点离页面顶部的距离为" + data.top);
  })
  .exec();

注意

  • 支付宝小程序不支持 in(component),使用无效果
selectorQuery.select(selector)

在当前页面下选择第一个匹配选择器 selector 的节点,返回一个 NodesRef 对象实例,可以用于获取节点信息。

selector 说明:

selector 类似于 CSS 的选择器,但仅支持下列语法。

  • ID 选择器: #the-id
  • class 选择器(可以连续指定多个): .a-class.another-class
  • 子元素选择器: .the-parent > .the-child
  • 后代选择器: .the-ancestor .the-descendant
  • 跨自定义组件的后代选择器: .the-ancestor >>> .the-descendant (H5 暂不支持)
  • 多选择器的并集: #a-node, .some-other-nodes
selectorQuery.selectAll(selector)

在当前页面下选择匹配选择器 selector 的所有节点,返回一个 NodesRef 对象实例,可以用于获取节点信息。

selectorQuery.selectViewport()

选择显示区域,可用于获取显示区域的尺寸、滚动位置等信息,返回一个 NodesRef 对象实例。

selectorQuery.exec(callback)

执行所有的请求。请求结果按请求次序构成数组,在 callback 的第一个参数中返回。

NodesRef

用于获取节点信息的对象

nodesRef.fields(object,callback)

获取节点的相关信息。第一个参数是节点相关信息配置(必选);第二参数是方法的回调函数,参数是指定的相关节点信息。

object 参数说明

字段名类型默认值必填说明平台差异说明
idBooleanfalse是否返回节点 id
datasetBooleanfalse是否返回节点 datasetApp、微信小程序、H5
rectBooleanfalse是否返回节点布局位置( left right top bottom
sizeBooleanfalse是否返回节点尺寸( width height
scrollOffsetBooleanfalse是否返回节点的 scrollLeft scrollTop ,节点必须是 scroll-view 或者 viewport
propertiesArray<string>[]指定属性名列表,返回节点对应属性名的当前属性值(只能获得组件文档中标注的常规属性值,id class style 和事件绑定的属性值不可获取)仅 App 和微信小程序支持
computedStyleArray<string>[]指定样式名列表,返回节点对应样式名的当前值仅 App 和微信小程序支持
contextBooleanfalse是否返回节点对应的 Context 对象仅 App 和微信小程序支持

editorContext.format(name, value)

修改样式

参数类型说明
nameString属性
valueString

支持设置的样式列表

namevalue平台差异说明
bold
italic
underline
strike
ins
scriptsub / super
headerH1 / H2 / h3 / H4 / h5 / H6
alignleft / center / right / justifyleft百度小程序不支持
directionrtl
indent-1 / +1
listordered / bullet / check
colorhex color
backgroundColorhex color
margin/marginTop/marginBottom/marginLeft/marginRightcss style百度小程序不支持
padding/paddingTop/paddingBottom/paddingLeft/paddingRightcss style百度小程序不支持
font/fontSize/fontStyle/fontVariant/fontWeight/fontFamilycss style百度小程序不支持
lineHeightcss style百度小程序不支持
letterSpacingcss style百度小程序不支持
textDecorationcss style百度小程序不支持
textIndentcss style百度小程序不支持
wordWrapcss style百度小程序不支持
wordBreakcss style百度小程序不支持
whiteSpacecss style百度小程序不支持

对已经应用样式的选区设置会取消样式。css style 表示 css 中规定的允许值。

editorContext.insertDivider(OBJECT)

插入分割线

OBJECT 参数说明

属性类型默认值必填说明
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

editorContext.insertImage(OBJECT)

插入图片。

微信小程序平台地址为临时文件时,获取的编辑器html格式内容中 <img> 标签增加属性 data-local,delta 格式内容中图片 attributes 属性增加 data-local 字段,该值为传入的临时文件地址。 开发者可选择在提交阶段上传图片到服务器,获取到网络地址后进行替换。替换时对于html内容应替换掉 <img> 的 src 值,对于 delta 内容应替换掉 insert { image: abc } 值。

OBJECT 参数说明

属性类型默认值必填说明
srcString图片地址,仅支持 http(s)、base64、本地图片
altString图像无法显示时的替代文本
widthString图片宽度(pixels/百分比),2.6.5+ 支持,百度小程序不支持
heightString图片高度 (pixels/百分比),2.6.5+ 支持,百度小程序不支持
extClassString添加到图片 img 标签上的类名,2.6.5+ 支持
dataObjectdata 被序列化为 name=value;name1=value2 的格式挂在属性 data-custom 上,2.6.5+ 支持
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

editorContext.getContents(OBJECT)

获取编辑器内容

OBJECT 参数说明

属性类型默认值必填说明
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

object.success 回调函数

属性类型说明
htmlstring带标签的 HTML 内容
textstring纯文本内容
deltaObject表示内容的 delta 对象

完整例子

<template>
	<view class="edit">
		<view class="title">
			<input type="text" v-model="artData.title" placeholder="请输入完整的标题" placeholder-class="placeholderClass" />
		</view>
		<view class="content">
			<editor
				class="myEdit"
				placeholder="写点什么吧~~"
				show-img-resize
				show-img-toolbar
				show-img-size
				@ready="onEditReady"
				@focus="OnFocus"
				@statuschange="statuschange"
			></editor>
		</view>
		<view class="btnGroup">
			<u-button type="primary" text="确认发表" @click="onSubmit" :disabled="!artData.title"></u-button>
		</view>
		<view class="tools" v-show="toolsShow">
			<!-- 标题 -->
			<view class="item" @click="headChange">
				<text class="iconfont icon-zitibiaoti" :class="headShow ? 'active' : ''"></text>
			</view>
			<!-- 加粗 -->
			<view class="item" @click="boldChange">
				<text class="iconfont icon-zitijiacu" :class="boldShow ? 'active' : ''"></text>
			</view>
			<!-- 斜体 -->
			<view class="item" @click="italicChange">
				<text class="iconfont icon-zitixieti" :class="italicShow ? 'active' : ''"></text>
			</view>
			<!-- 分割线 -->
			<view class="item" @click="insertDivider"><text class="iconfont icon-fengexian"></text></view>
			<view class="item" @click="insertImage"><text class="iconfont icon-charutupian"></text></view>
			<!-- 完成 -->
			<view class="item" @click="editOk"><text class="iconfont icon-duigou_kuai"></text></view>
		</view>
	</view>
</template>
 
<script>
export default {
	data() {
		return {
			toolsShow: false,
			editorContent: null,
			headShow: false,
			boldShow: false,
			italicShow: false,
			artData: {
				title: '',
				content: ''
			}
		};
	},
	methods: {
		// 初始化
		onEditReady() {
			// 查询节点信息的对象
			// 将选择器的选取范围更改为自定义组件 component 内,返回一个 SelectorQuery 对象实例。(初始时,选择器仅选取页面范围的节点,不会选取任何自定义组件中的节点)。
			// 在当前页面下选择第一个匹配选择器 selector 的节点,返回一个 NodesRef 对象实例,可以用于获取节点信息。
			// 获取节点的相关信息。第一个参数是节点相关信息配置(必选);第二参数是方法的回调函数,参数是指定的相关节点信息。
			// 执行所有的请求。请求结果按请求次序构成数组,在 callback 的第一个参数中返回。
			uni.createSelectorQuery()
				.in(this)
				.select('.myEdit')
				.fields(
					{
						size: true, //是否返回节点尺寸(width height)
						context: true //是否返回节点尺寸(width height)
					},
					(res) => {
						console.log(res);
						this.editorContent = res.context;
					}
				)
				.exec();
		},
		// 离开焦点
		OnFocus() {
			this.toolsShow = true;
		},
		// 添加分割线
		insertDivider() {
			this.editorContent.insertDivider();
		},
		// 添加大标题
		headChange() {
			this.headShow = !this.headShow;
			this.editorContent.format('header', this.headShow ? 'H2' : false);
		},
		// 加粗
		boldChange() {
			this.boldShow = !this.boldShow;
			this.editorContent.format('bold');
		},
		// 斜体
		italicChange() {
			this.italicShow = !this.italicShow;
			this.editorContent.format('italic');
		},
		// 用于检查对象detail是否包含名为name的属性
		checkStatus(name, detail, key) {
			if (detail.hasOwnProperty(name)) {
				this[key] = true;
			} else {
				this[key] = false;
			}
		},
		// 通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式(解决标题标签回弹问题)
		statuschange(e) {
			let detail = e.detail;
			console.log(detail);
			this.checkStatus('header', detail, 'headShow');
			this.checkStatus('bold', detail, 'boldShow');
			this.checkStatus('italic', detail, 'italicShow');
		},
		// 添加图像
		insertImage() {
			// 打开相机
			uni.chooseImage({
				count: 3, //默认9
				sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
				success: async (res) => {
					let tempFiles = res.tempFiles;
					for (let item of tempFiles) {
						uni.showLoading({
							mask: true,
							title: '上传中...'
						});
						try {
							let res = await uniCloud.uploadFile({
								filePath: item.path, //要上传的文件对象
								cloudPath: item.name //cloudPath为云端文件名,
							});
							// 设置图片地址 图片地址,仅支持 http(s)、base64、本地图片
							this.editorContent.insertImage({
								src: res.fileID
							});
							uni.hideLoading();
						} catch (e) {
							uni.hideLoading();
							uni.showModal({
								content: '上传失败',
								title: '提示'
							});
						}
					}
				}
			});
		},
		// 确认
		editOk() {
			this.toolsShow = false;
		},
		onSubmit() {
			console.log(11);
			this.editorContent.getContents({
				success: (res) => {
					console.log(res);
					this.artData.content = res.html;
				}
			});
		}
	}
};
</script>
 
<style lang="scss">
/deep/ .ql-blank::before {
	font-style: normal;
	color: #e0e0e0;
}
 
.edit {
	padding: 30rpx;
	.title {
		input {
			height: 120rpx;
			font-size: 46rpx;
			border-bottom: 1px solid #e4e4e4;
			margin-bottom: 30rpx;
			color: #000;
		}
		.placeholderClass {
			color: #e0e0e0;
		}
	}
	.content {
		.myEdit {
			height: calc(100vh - 500rpx);
			margin-bottom: 30rpx;
		}
	}
	.tools {
		width: 100%;
		height: 80rpx;
		background: #fff;
		border-top: 1rpx solid #f4f4f4;
		position: fixed;
		left: 0;
		bottom: 0;
		display: flex;
		justify-content: space-around;
		align-items: center;
		.iconfont {
			font-size: 36rpx;
			color: #333;
			// 选择按钮的高亮
			&.active {
				color: #0199fe;
			}
		}
	}
}
</style>