本文将主要讲解一下若是经过微信小程序来实现封装一个富文本编辑器 Editor,可拿来即用。javascript
developers.weixin.qq.com/miniprogram…css
developers.weixin.qq.com/miniprogram…html
richTest.wxmljava
<view class="whole" id="richText">
<view style="height:{{textTool?'200':'100'}}rpx;"></view>
<view class="page-body">
<view class='wrapper'>
<editor id="editor" class="ql-container" placeholder="{{placeholder}}" showImgSize showImgToolbar showImgResize bindstatuschange="onStatusChange" read-only="{{readOnly}}" bindready="onEditorReady" bindfocus='bindfocus' bindblur='bindblur' bindinput='bindinput' >
</editor>
</view>
</view>
<view class="editor-toolbar" bindtap="format">
<view class="toolbar-2">
<view class="tool-item-cell">
<view class="tool-item-box">
<view class="cell-rg-shadow"></view>
<scroll-view scroll-x class="flex-sb" style="height:70rpx;white-space: nowrap;" >
<view class="tool-item">
<i class="iconfont icon-charutupian" data-tool_name='insertImage' bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-font" data-tool_name='showTextTool' bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-format-header-1 {{formats.header === 1 ? 'ql-active' : ''}}" data-tool_name='text_H1' data-name="header" data-value="{{1}}" bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-date" data-tool_name='insertDate' bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-undo" data-tool_name='undo' bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-redo" data-tool_name='redo' bindtap="toolEvent"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-shanchu" data-tool_name='clear' bindtap="toolEvent"></i>
</view>
</scroll-view>
</view>
</view>
<lable class='save-icon' style='background:{{appColorConfig.check_color}}' bindtap="getEditorContent" >
{{buttonTxt}}
</lable>
</view>
<view class="toolbar-1" wx:if="{{textTool}}">
<scroll-view scroll-x style="height:70rpx;white-space: nowrap;" >
<view class="tool-item">
<i class="iconfont icon-zitijiacu {{formats.bold ? 'ql-active' : ''}}" data-name="bold"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-zitixieti {{formats.italic ? 'ql-active' : ''}}" data-name="italic"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-zitixiahuaxian {{formats.underline ? 'ql-active' : ''}}" data-name="underline"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-fengexian" bindtap='insertDivider'></i>
</view>
<view class="tool-item">
<i class="iconfont icon-zuoduiqi {{formats.align === 'left' ? 'ql-active' : ''}}" data-name="align" data-value="left"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-juzhongduiqi {{formats.align === 'center' ? 'ql-active' : ''}}" data-name="align" data-value="center"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-youduiqi {{formats.align === 'right' ? 'ql-active' : ''}}" data-name="align" data-value="right"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-zuoyouduiqi {{formats.align === 'justify' ? 'ql-active' : ''}}" data-name="align" data-value="justify"></i>
</view>
<view class="tool-item">
<i class="iconfont icon--checklist" data-name="list" data-value="check"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-youxupailie {{formats.list === 'ordered' ? 'ql-active' : ''}}" data-name="list" data-value="ordered"></i>
</view>
<view class="tool-item">
<i class="iconfont icon-wuxupailie {{formats.list === 'bullet' ? 'ql-active' : ''}}" data-name="list" data-value="bullet"></i>
</view>
</scroll-view>
</view>
</view>
</view>
复制代码
richTest.wxssgit
@import "./assets/iconfont.wxss";
page {
background: #f8f8f8;
}
.page-body{
padding-bottom: 100rpx;
}
.editor-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 9999;
}
.editor-toolbar i {
display: flex;
align-items: center;
justify-content: center;
}
.toolbar-1 {
padding: 5rpx 0;
background: #e4e4e4;
}
.editor-toolbar .tool-item {
display: inline-block;
}
.toolbar-2 {
padding: 5rpx 20px 5rpx 10px;
background: #f4f4f4;
display: flex;
align-items: center;
justify-content:space-between;
position: relative;
}
.toolbar-2 .tool-item-cell{
max-width: 80%;
}
.toolbar-2 .tool-item-box{
position: relative;
}
.toolbar-2 .cell-rg-shadow{
position: absolute;
right: 0;
top: 0;
width: 1px;
height: 100%;
z-index: 999;
background:#dddddd;
}
.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;
}
.ql-container {
box-sizing: border-box;
padding: 12px 15px;
width: 100%;
min-height: 30vh;
height: auto;
background: #fff;
font-size: 16px;
line-height: 1.5;
}
.ql-active {
color: #06c;
}
.save-icon {
padding: 15rpx 30rpx;
font-size: 20rpx;
background: #bf98d2;
color: #fff;
}
.flex{
display: flex;
}
.flex-cc{
display: flex;
align-items: center;
-ms-flex-item-align: center;
justify-content: center;
}
.flex-sb{
display: flex;
align-items: center;
-ms-flex-item-align: center;
justify-content: space-between;
}
.flex-sa{
display: flex;
align-items: center;
-ms-flex-item-align: center;
justify-content: space-around;
}
复制代码
richTest.jsgithub
富文本工具栏点击事件json
toolEvent(res) {
let { tool_name } = res.currentTarget.dataset;
switch (tool_name) {
case 'insertImage': // 插入图片
this.insertImageEvent();
break;
case 'showTextTool': // 展现文字编辑工具
this.showTextTool();
break;
case 'insertDate': // 插入日期
this.insertDate();
break;
case 'undo': // 撤退(向前)
this.undo();
break;
case 'redo': // 撤退(向后)
this.restore();
break;
case 'clear': // 清除
this.clearBeforeEvent();
break;
}
},
复制代码
编辑器初始化完成时触发小程序
onEditorReady() {
console.log('编辑器初始化完成时触发')
this.triggerEvent('onEditorReady');
// 返回一个 SelectorQuery 对象实例。在自定义组件或包含自定义组件的页面中,应使用this.createSelectorQuery()来代替。
// 对应API:https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html
this.createSelectorQuery().select('#editor').context(res => {
console.log('createSelectorQuery=>', res)
this.editorCtx = res.context;
let rtTxt = '';
this.setContents(rtTxt); // 设置富文本内容
}).exec();
},
复制代码
事件方法微信小程序
// 设置富文本内容
setContents(rechtext) {
this.editorCtx.setContents({
html: rechtext,
success: res => {
console.log('[setContents success]', res)
}
})
},
// 撤销
undo() {
this.editorCtx.undo();
this.triggerEvent('undo');
},
// 恢复
restore() {
this.editorCtx.redo();
this.triggerEvent('restore');
},
// 清空编辑器内容
clear() {
this.editorCtx.clear({
success: res => {
this.triggerEvent('clearSuccess');
}
})
},
//清空编辑器内容前的事件
clearBeforeEvent() {
this.triggerEvent('clearBeforeEvent');
},
//清除当前选区的样式
removeFormat() {
this.editorCtx.removeFormat();
},
//插入图片事件
insertImageEvent() {
//触发父组件选择图片方法
this.triggerEvent('insertImageEvent', {});
},
//插入日期
insertDate() {
if (supportDateFormat.indexOf(this.data.formatDate) < 0) {
console.error(`Format Date ${this.data.formatDate} error \n It should be one of them [${supportDateFormat}]`)
return;
}
let formatDate = this.getThisDate(this.data.formatDate);
this.editorCtx.insertText({
text: formatDate
})
},
//show展现文本工具栏
showTextTool() {
this.setData({
textTool: !this.data.textTool
})
},
//保存按钮事件,获取编辑器内容
getEditorContent() {
this.editorCtx.getContents({
success: res => {
// console.log('[getContents rich text success]', res)
this.triggerEvent('getEditorContent', {
value: res,
});
}
})
},
复制代码
编辑器事件api
//编辑器聚焦时触发
bindfocus(res) {
this.triggerEvent('bindfocus', {
value: res,
});
},
//编辑器失去焦点时触发
bindblur(res) {
this.triggerEvent('bindblur', {
value: res,
});
},
//编辑器输入中时触发
bindinput(res) {
this.triggerEvent('bindinput', {
value: res,
});
},
复制代码
插入图片方法
/** * 插入图片方法 * @param {String} path 图片地址,仅支持 http(s)、base6四、云图片(2.8.0)、临时文件(2.8.3) */
insertImageMethod(path) {
return new Promise((resolve, reject) => {
this.editorCtx.insertImage({
src: path,
data: {
id: 'imgage',
},
success: res => {
resolve(res);
},
fail: res => {
reject(res);
}
})
})
},
复制代码
返回当前日期的方法
/** * 返回当前日期 * @format {String} 须要返回的日期格式 */
getThisDate(format) {
let date = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
h = date.getHours(),
m = date.getMinutes();
//数值补0方法
const zero = (value) => {
if (value < 10) return '0' + value;
return value;
}
switch (format) {
case 'YY-MM':
return year + '-' + zero(month);
case 'YY.MM.DD':
return year + '.' + zero(month) + '.' + zero(day);
case 'YY-MM-DD':
return year + '-' + zero(month) + '-' + zero(day);
case 'YY.MM.DD HH:MM':
return year + '.' + zero(month) + '.' + zero(day) + ' ' + zero(h) + ':' + zero(m);
case 'YY/MM/DD HH:MM':
return year + '/' + zero(month) + '/' + zero(day) + ' ' + zero(h) + ':' + zero(m);
case 'YY-MM-DD HH:MM':
return year + '-' + zero(month) + '-' + zero(day) + ' ' + zero(h) + ':' + zero(m);
default:
return year + '/' + zero(month) + '/' + zero(day);
}
}
复制代码
index.js
编辑器初始化完成时触发,能够获取组件实例
onEditorReady() {
console.log('[onEditorReady callback]')
richText = this.selectComponent('#richText'); //获取组件实例
},
复制代码
功能点实现
//设置富文本内容
setContents(rechtext) {
this.editorCtx.setContents({
html: rechtext,
success: res => {
console.log('[setContents success]', res)
}
})
},
//插入图片
insertImageEvent() {
wx.chooseImage({
count: 1,
success: res => {
let path = res.tempFilePaths[0];
//调用子组件方法,图片应先上传再插入,否则预览时没法查看图片。
richText.insertImageMethod(path).then(res => {
console.log('[insert image success callback]=>', res)
}).catch(res => {
console.log('[insert image fail callback]=>', res)
});
}
})
},
//保存,获取编辑器内容
getEditorContent(res) {
let { value } = res.detail;
wx.showToast({
title: '获取编辑器内容成功',
icon: 'none',
})
console.log('[getEditorContent callback]=>', value)
},
复制代码
index.wxml
<richText id='richText' readOnly='{{readOnly}}' placeholder='{{placeholder}}' formatDate='YY/MM/DD' buttonTxt='保存' bind:clearBeforeEvent='clearBeforeEvent' bind:clearSuccess='clearSuccess' bind:undo='undo' bind:restore='restore' bind:onEditorReady='onEditorReady' bind:bindfocus='bindfocus' bind:bindblur='bindblur' bind:bindinput='bindinput' bind:insertImageEvent='insertImageEvent' bind:getEditorContent='getEditorContent'></richText>
<view class="tip">备注:
<view>1.改变图片大小,按住节点一小会儿再拖动。</view>
<view>2.预览内容中,图片仅支持网络url。</view>
</view>
<view class="preview" bindtap="preview">预览</view>
复制代码
欢迎 Star GitHub: https://github.com/jxh1997/Editor 。
因此源代码均在 Github 上,下载便可使用。
git clone https://github.com/jxh1997/Editor.git
复制代码
在下载源码中找到组件目录:components/richText
,将 richText 整个文件夹复制到你的项目中。
在 page.json
中引入组件
"usingComponents": {
"richText":"../../components/richText/richText"
},
复制代码
在 page.wxml
中使用组件
<richText
id='richText'
readOnly='{{readOnly}}'
placeholder='{{placeholder}}'
formatDate='YY/MM/DD'
buttonTxt='保存'
bind:clearBeforeEvent='clearBeforeEvent'
bind:clearSuccess='clearSuccess'
bind:undo='undo'
bind:restore='restore'
bind:onEditorReady='onEditorReady'
bind:bindfocus='bindfocus'
bind:bindblur='bindblur'
bind:bindinput='bindinput'
bind:insertImageEvent='insertImageEvent'
bind:getEditorContent='getEditorContent'
> </richText>
复制代码
在 page.js
中,接受富文本编辑器相关事件。具体执行代码如上。