微信小程序之實現封裝一個富文本編輯器 Editor 的完整流程【附demo源碼】歡迎點贊收藏

本文將主要講解一下若是經過微信小程序來實現封裝一個富文本編輯器 Editor,可拿來即用。javascript

1、官方文檔:

editor(富文本編輯器,能夠對圖片、文字進行編輯):

developers.weixin.qq.com/miniprogram…css

rich-text(富文本):

developers.weixin.qq.com/miniprogram…html

2、效果圖:

在這裏插入圖片描述

3、代碼詳解:

1. 編輯器界面設計

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>
複製代碼

2. 編輯器界面樣式

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;
}
複製代碼

3. 編輯器業務邏輯

richTest.jsgithub

  1. 富文本工具欄點擊事件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;
      }
    },
    複製代碼
  2. 編輯器初始化完成時觸發小程序

    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();
    },
    複製代碼
  3. 事件方法微信小程序

    // 設置富文本內容
    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,
          });
        }
      })
    },
    複製代碼
  4. 編輯器事件api

    //編輯器聚焦時觸發
    bindfocus(res) {
      this.triggerEvent('bindfocus', {
        value: res,
      });
    },
    
    //編輯器失去焦點時觸發
    bindblur(res) {
      this.triggerEvent('bindblur', {
        value: res,
      });
    },
    
    //編輯器輸入中時觸發
    bindinput(res) {
      this.triggerEvent('bindinput', {
        value: res,
      });
    },
    複製代碼
  5. 插入圖片方法

    /** * 插入圖片方法 * @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);
          }
        })
      })
    },
    複製代碼
  6. 返回當前日期的方法

    /** * 返回當前日期 * @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);
      }
    }
    複製代碼

4. 接受富文本編輯器相關事件

index.js

  1. 編輯器初始化完成時觸發,能夠獲取組件實例

    onEditorReady() {
        console.log('[onEditorReady callback]')
        richText = this.selectComponent('#richText'); //獲取組件實例
      },
    複製代碼
  2. 功能點實現

    //設置富文本內容
    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)
    },
    複製代碼

5. 使用富文本編輯器組件

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>
複製代碼

4、demo源碼:

歡迎 Star GitHub: https://github.com/jxh1997/Editor

因此源代碼均在 Github 上,下載便可使用。

1. 下載源碼

git clone https://github.com/jxh1997/Editor.git
複製代碼

2. 使用說明

  1. 在下載源碼中找到組件目錄:components/richText,將 richText 整個文件夾複製到你的項目中。

  2. page.json 中引入組件

    "usingComponents": {
        "richText":"../../components/richText/richText"
    },
    複製代碼
  3. 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>
    複製代碼
  4. page.js 中,接受富文本編輯器相關事件。具體執行代碼如上。


若是以上內容對你的學習工做有幫助,歡迎點贊收藏,若是以爲 Github 上的代碼還不錯,歡迎前往 Star

相關文章
相關標籤/搜索