微信小程序开发和editor组件件封装

307 阅读3分钟

JSON.stringify JSON.parse

微信小程序中由于对象中会有文字,有些文字是包含特殊字符的 比如'\n' stringify之后再转parse回报错 解决方式: 1:需要保证对象中没有\n 2:或者先 decodeURIComponent 再 encodeURIComponent

关于wx.canvasToTempFilePath

使用 Canvas 绘图成功后,直接调用该方法生成图片,在IDE上没有问题,但在真机上会出现生成的图片不完整的情况,可以使用一个setTimeout来解决这个问题。

 setTimeout(() => {
    wx.canvasToTempFilePath({
      canvasId: 'canvasid',
      success: async(res) => {
        this.props.onSavePoster(res.tempFilePath)//回调事件
        // 清空画布
        this.ctx.clearRect(0, 0, canvas_width, canvas_height)
      },
      fail: (err) => {
        console.log(err)
      }
    }, this.$scope)
  }, 0)

editor插件封装

<!--components/editor-html/index.wxml-->
<view class="editor-box">
  <view class="editor-box-header" wx:if="{{showTabBar}}"  bind:tap="format">
    <!-- <view class="operate-box" data-uploadImageURL="{{uploadImageURL}}" bind:tap="_addImage">
      <text class="iconfont icon-image"></text>
    </view> -->
    <view class="operate-box">
        <van-uploader accept="image" max-count="1" bind:after-read="afterRead">
            <text class="iconfont icon-image"></text>
        </van-uploader>
    </view>
    <view class="operate-box" bind:tap="_addItalic">
      <text class="iconfont icon-italic"></text>
    </view>
    <view class="operate-box" bind:tap="_addBold">
      <text class="iconfont icon-bold"></text>
    </view>
    <view class="operate-box" data-header="h1" bind:tap="_addHeader">
      <text class="iconfont icon-h1"></text>
    </view>
    <view class="operate-box" data-header="h2" bind:tap="_addHeader">
      <text class="iconfont icon-h2"></text>
    </view>
    <view class="operate-box" data-header="h3" bind:tap="_addHeader">
      <text class="iconfont icon-h3"></text>
    </view>
    <view class="operate-box" data-align="left" bind:tap="_addAlign">
      <text class="iconfont icon-alignLeft"></text>
    </view>
    <view class="operate-box" data-align="right" bind:tap="_addAlign">
      <text class="iconfont icon-alignRight"></text>
    </view>
    <view class="operate-box" data-list="ordered" bind:tap="_addList">
      <text class="iconfont icon-orderedList"></text>
    </view>
    <view class="operate-box" data-list="bullet" bind:tap="_addList">
      <text class="iconfont icon-unorderedList"></text>
    </view>
    <view class="operate-box" bind:tap="_undo">
      <text class="iconfont icon-undo"></text>
    </view>
    <i class="iconfont icon-clearedformat" bindtap="removeFormat"></i>
        <i class="iconfont icon-shanchu" bindtap="clear"></i>


  </view>
  <view class="editor-box-content">
        <editor id="editor" class="ql-container" style="height:{{height}}" name="{{name}}" placeholder="{{placeholder}}" bind:ready="_onEditorReady"
        bind:statuschange="_onStatusChange"
        bind:input="_onInputting" show-img-resize="{{true}}" show-img-toolbar="{{true}}" show-img-resize="{{true}}"></editor>
  </view>
</view>
{
  "component": true,
  "usingComponents": {
    "van-uploader": "@vant/weapp/uploader/index"
  }
}
@font-face {
  font-family: 'iconfont';  /* Project id 2549449 */
  src: url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.woff2?t=1621002720450') format('woff2'),
       url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.woff?t=1621002720450') format('woff'),
       url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.ttf?t=1621002720450') format('truetype');
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 38rpx;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-undo:before {
  content: "\e609";
}

.icon-hr:before {
  content: "\e60a";
}

.icon-h3:before {
  content: "\e60b";
}

.icon-quote:before {
  content: "\e60c";
}

.icon-bold:before {
  content: "\e60e";
}

.icon-orderedList:before {
  content: "\e612";
}

.icon-h2:before {
  content: "\e61a";
}

.icon-italic:before {
  content: "\e61c";
}

.icon-unorderedList:before {
  content: "\e620";
}

.icon-alignLeft:before {
  content: "\e621";
}

.icon-alignRight:before {
  content: "\e622";
}

.icon-h1:before {
  content: "\e623";
}

.icon-image:before {
  content: "\e629";
}


/* components/editor-html/index.wxss */
@import "iconfont.wxss";

.editor-box {
  width: 100%;
  padding: 0;
  box-sizing: border-box;
  /* background-color: #fff; */
  /* border:2px solid #f6f6f6; */
  /* height: 588rpx; */
  background: #F8F8F8;
  border-radius: 8rpx;
  border: 1rpx solid #EEEEEE;  
}

.editor-box-header,
.editor-box-content {
  width: 100%;
}

.editor-box-header {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  justify-content:flex-start;
  padding: 20rpx 20rpx;
  box-sizing: border-box;
  border-bottom: 2rpx solid #f6f6f6;
  background-color: #f6f6f6;
}

.editor-box-header>.operate-box {
  margin-right: 20rpx;
  width: 40rpx;
  height: 40rpx;
  overflow: hidden;
  color:gray;
}

.editor-box-content{
  padding:20rpx;
  box-sizing: border-box;
}

// components/editor-html/index.js
/*
 * @Author: zhangyu
 * @Email: zhangdulin@outlook.com
 * @Date: 2022-09-14 10:57:58
 * @LastEditors: zhangyu
 * @LastEditTime: 2022-09-16 09:42:45
 * @Description: 
 */

const {  uploadMaterial } = require('../../utils/services/material');
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    /**是否显示工具栏 */
    showTabBar: {
      type: 'Boolean',
      value: true
    },
    height: {
        type: 'String',
        value: '200rpx'
    },  
    placeholder: {
      type: 'String',
      value: '请输入相关内容'
    },
    name: {
      type: 'String',
      value: ''
    },
    uploadImageURL: {
      type: 'String',
      value: ''
    },
    // 修改时显示内容
    richTextContents: {
        type: String,
        value: "",
    },
    // 编辑的富文本的索引
    index: {
        type: Number,
        value: 0,
    },
    // 默认内容
    defaultContent: {
        type: String,
        value: "",
        observer(val) {
            val && this.initData()   // 当visible变为true的时候 会触发initData
         }
    },
  },

  observers: {
    visible(val) {
      console.log(val) // 这里会一直监听着的
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    formats:''
  },

  /**
   * 组件的方法列表
   */
  methods: {
    initData() {
        wx.nextTick(() => {
            setTimeout(()=>{
                this.properties.defaultContent && this.setContents(this.properties.defaultContent); //设置富文本内容
            },1500)
        })
    },
    // 编辑器初始化完成时触发 
    _onEditorReady: function () {
      const that = this;
      // 通知父组件
      that.triggerEvent('onEditorReady');
      that.createSelectorQuery().select('#editor').context(function (res) {
        that.editorCtx = res.context
        // if (wx.  ("content")) { // 设置~历史值
        //     that.editorCtx.insertText(wx.getStorageSync("content")) // 注意:插入的是对象
        // }
        // that.properties.defaultContent && that.setContents(that.properties.defaultContent); //设置富文本内容
      }).exec()
    },
    // 设置富文本内容
    setContents(rechtext) {
      this.editorCtx.setContents({
        html: rechtext,
        success: (res) => {
          // 富文本内容设置成功
          // console.log("[setContents success]", res);
        },
      });
    },
    //监控输入  富文本编辑器输入时,获取值
    _onInputting(e) {
      let html = e.detail.html;
      let text = e.detail.text;
      this.triggerEvent("getEditorValue", { html: html, text: text });
      wx.setStorageSync("content", html); // 缓存本地
    },
    // 工具栏选项选中,图标出现选中样式
    _onStatusChange(e) {
        let self = this;
        self.setData({
          formats: e.detail,
        });
    },
    //插入图片 van-upload
    afterRead(event) {
        const { file } = event.detail;
        wx.showLoading({
            title: '上传中',
            mask: true
          });
        uploadMaterial(file).then(res => {
            wx.hideLoading()
            try {
                const { data } = res;
                this.editorCtx.insertImage({
                  src: data.fullPath
                });
            } catch (error) {
                wx.showToast({
                    title: '图片上传失败',
                    icon: 'none'
                })
            }
        })
    },
    //插入图片
    _addImage: function (event) {
      let _this = this;
      wx.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['album'],
        success: function (res) {
        //   wx.showLoading({
        //     title: '上传中',
        //     mask: true
        //   });

        //   _this._uploadImage(res.tempFilePaths[0], event.currentTarget.dataset.uploadimageurl);
        }
      });
    },
    // _uploadImage: function (tempFilePath, uploadImageURL) {
    //   let _this = this;
    //   wx.uploadFile({
    //     filePath: tempFilePath,
    //     name: 'image',
    //     url: uploadImageURL,
    //     success: function (res) {
    //       res = JSON.parse(res.data);
    //       wx.hideLoading({
    //         success: () => {
    //           if (res.code === 200) {
    //             _this.editorCtx.insertImage({
    //               src: res.data
    //             });
    //           } else {
    //             wx.showToast({
    //               icon: 'error',
    //               title: '服务器错误,稍后重试!',
    //               mask: true
    //             })
    //           }
    //         },
    //       });
    //     }
    //   });
    // },
    //设置斜体
    _addItalic: function () {
      this.editorCtx.format("italic")
    },
    //添加粗体样式
    _addBold: function () {
      this.editorCtx.format("bold")
    },
    //设置标题
    _addHeader: function (e) {
      let headerType = e.currentTarget.dataset.header;
      this.editorCtx.format("header", headerType)
    },
    //设置文字的排列方式
    _addAlign: function (e) {
      let alignType = e.currentTarget.dataset.align;
      this.editorCtx.format("align", alignType);
    },
    //设置列表
    _addList: function (e) {
      let listType = e.currentTarget.dataset.list;
      this.editorCtx.format("list", listType);
    },
    //撤销
    _undo: function () {
      this.editorCtx.undo();
    },
    // 清空所有
    clear() {
        this.editorCtx.clear({
        success: function(res) {
            console.log("清空成功")
        }
        })
    },
    // 清除样式
    removeFormat() {
        this.editorCtx.removeFormat()
    },
    // 记录样式 点击工具栏格式化编辑文本
    format(e) {
        let {
        name,
        value
        } = e.target.dataset
        if (!name) return
        // 富文本编辑器格式化内容方法
        this.editorCtx.format(name, value)
    },
  }
})