AdaptiveToast.js

import {delay, deepAssign} from './operationKit';

/**
 * 自适应的toast
 * 处理原生toast截断问题,详见{@tutorial 3.1-adaptiveToast}
 */
class AdaptiveToast {
  _options = {
    icons: {
      success: '/images/toast/success.png',
      fail: '/images/toast/fail.png'
    },
    defaultOpts: {
      title: '',
      type: 'fail',
      duration: 2000,
    },
    installProps: {
      '$toast': 'toast'
    }
  };

  /**
   * 构造函数
   * @param {object} [options] 配置参数
   * @param {Object.<string, string>} [options.icons={success: '/images/toast/success.png',fail: '/images/toast/fail.png'}] 图标映射表,key为调用方指定的toast场景类型,value为对应的图标路径
   * @param {AdaptiveToast~ToastOptions} [options.defaultOpts={title: '',type: 'fail',duration: 2000}] toast的默认选项
   */
  constructor(options){
    deepAssign(this._options, options);
  }

  /**
   * @ignore
   */
  get installProps(){
    return this._options.installProps;
  }

  /**
   * 自适应的toast,会自动根据文案长度选择合适的提示方式
   * @function
   * @async
   * @param {AdaptiveToast~ToastOptions} options toast参数
   */
  toast = async (options)=>{
    options = Object.assign({}, this._options.defaultOpts, options);

    let len = options.title.length;
    if (len <= 7) //文案简洁,使用带图标的toast
      return this.sysToastIcon(options);
    else if (len <= 20) //文案较长,使用长文本toast
      return this.sysToastText(options);
    else //文案巨长,改用弹窗
      return this.sysToastModal(options);
  }
  
  /**
   * 文案较少时使用的toast,带图标,最多只能展示7个汉字
   * @function
   * @async
   * @param {AdaptiveToast~ToastOptions} options toast参数
   */
  sysToastIcon = async (options)=>{
    wx.showToast({
      title: options.title,
      image: this._options.icons[options.type] || options.type,
      duration: options.duration,
      success : options.success,
      fail:options.fail,
      complete:options.complete
    });
    await delay(options.duration);
  }
  
  /**
   * 文案中等长度时使用的toast,不带图标,最多展示两行
   * @function
   * @async
   * @param {AdaptiveToast~ToastOptions} options toast参数
   */
  sysToastText = async (options)=>{
    if (!wx.setTabBarItem) //不带图标的toast从基础库1.9.0开始支持;wx.canIUse('showToast.object.icon.none')不好使,暂借用其它API来判断版本
      return this.sysToastModal(options);

    let title = options.title;
    /*if (!title.includes('\n')) { //折成字数相等的两行 (安卓机下有时第一行会变成'...'不能正常展示,且与内容编码无关,纯英文字符串亦可复现;原因不明,暂去掉自动换行逻辑)
      let mid = Math.ceil(title.length/2);
      title = title.substring(0, mid)+'\n'+title.substring(mid);
    }*/

    wx.showToast({
      title,
      icon: 'none',
      duration: options.duration,
      success : options.success,
      fail:options.fail,
      complete:options.complete
    });
    await delay(options.duration);
  }
  
  /**
   * 文案巨长时使用的toast,自动改用系统弹窗
   * @function
   * @async
   * @param {AdaptiveToast~ToastOptions} options toast参数
   */
  sysToastModal = async (options)=>{
    return new Promise((resolve, reject)=>{
      wx.showModal({
        title: '提示',
        content: options.title,
        showCancel: false,
        confirmText: '知道了',
        success : options.success,
        fail:options.fail,
        complete: (...args)=>{
          options.complete && options.complete(...args);
          resolve();
        }
      });
    })
  }
}

/**
 * @typedef {object} AdaptiveToast~ToastOptions toast参数
 * @property {string} title toast文案
 * @property {string} [type] toast场景类型
 * @property {number} [duration] toast时长,单位:ms
 */

export default AdaptiveToast;